From d110847c188b0054adb09884412e5113f73f6950 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Fri, 2 May 2025 13:49:49 +1200 Subject: [PATCH 1/6] add ability to query specific txns in history --- swagger.yml | 5 ++ webserver/src/dto/transaction.rs | 2 + webserver/src/handler/transaction.rs | 2 +- webserver/src/repository/tranasaction.rs | 16 ++++- webserver/src/service/transaction.rs | 78 +++++++++++++++++++++++- 5 files changed, 98 insertions(+), 5 deletions(-) diff --git a/swagger.yml b/swagger.yml index 82b1138a9..fc8b84783 100644 --- a/swagger.yml +++ b/swagger.yml @@ -1004,6 +1004,11 @@ paths: minItems: 1 maxItems: 10 description: The list of address. Must contain at least 1 element + - in: query + name: transaction_types + schema: + type: string + description: Comma-separated list of transaction types to filter by. If not provided, all transaction types are returned. The options are transparentTransfer, shieldedTransfer, shieldingTransfer, unshieldingTransfer, mixedTransfer, ibcMsgTransfer, ibcTransparentTransfer, ibcShieldingTransfer, ibcUnshieldingTransfer, bond, redelegation, unbond, withdraw, claimRewards, voteProposal, initProposal, changeMetadata, changeCommission, revealPk, becomeValidator, reactivateValidator, deactivateValidator, unjailValidator responses: "200": description: Pagined historic transaction list. diff --git a/webserver/src/dto/transaction.rs b/webserver/src/dto/transaction.rs index 5f14eda21..4d2b3230f 100644 --- a/webserver/src/dto/transaction.rs +++ b/webserver/src/dto/transaction.rs @@ -8,4 +8,6 @@ pub struct TransactionHistoryQueryParams { pub page: Option, #[validate(length(min = 1, max = 10))] pub addresses: Vec, + // Optional comma-delimited string of transaction types to filter by + pub transaction_types: Option, } diff --git a/webserver/src/handler/transaction.rs b/webserver/src/handler/transaction.rs index 9676afcb1..628c9debc 100644 --- a/webserver/src/handler/transaction.rs +++ b/webserver/src/handler/transaction.rs @@ -74,7 +74,7 @@ pub async fn get_transaction_history( let (transactions, total_pages, total_items) = state .transaction_service - .get_addresses_history(query.addresses, page) + .get_addresses_history(query.addresses, page, query.transaction_types) .await?; let response = diff --git a/webserver/src/repository/tranasaction.rs b/webserver/src/repository/tranasaction.rs index a82a5cc39..c33179180 100644 --- a/webserver/src/repository/tranasaction.rs +++ b/webserver/src/repository/tranasaction.rs @@ -6,7 +6,7 @@ use orm::schema::{ inner_transactions, transaction_history, wrapper_transactions, }; use orm::transactions::{ - InnerTransactionDb, TransactionHistoryDb, WrapperTransactionDb, + InnerTransactionDb, TransactionHistoryDb, TransactionKindDb, WrapperTransactionDb, }; use super::utils::{Paginate, PaginatedResponseDb}; @@ -37,6 +37,7 @@ pub trait TransactionRepositoryTrait { &self, addresses: Vec, page: i64, + transaction_types: Option>, ) -> Result< PaginatedResponseDb<(TransactionHistoryDb, InnerTransactionDb, i32)>, String, @@ -108,6 +109,7 @@ impl TransactionRepositoryTrait for TransactionRepository { &self, addresses: Vec, page: i64, + transaction_types: Option>, ) -> Result< PaginatedResponseDb<(TransactionHistoryDb, InnerTransactionDb, i32)>, String, @@ -115,10 +117,20 @@ impl TransactionRepositoryTrait for TransactionRepository { let conn = self.app_state.get_db_connection().await; conn.interact(move |conn| { - transaction_history::table + let mut query = transaction_history::table .filter(transaction_history::dsl::target.eq_any(addresses)) .inner_join(inner_transactions::table.on(transaction_history::dsl::inner_tx_id.eq(inner_transactions::dsl::id))) .inner_join(wrapper_transactions::table.on(inner_transactions::dsl::wrapper_id.eq(wrapper_transactions::dsl::id))) + .into_boxed(); + + // Apply transaction type filter if provided + if let Some(types) = transaction_types { + if !types.is_empty() { + query = query.filter(inner_transactions::dsl::kind.eq_any(types)); + } + } + + query .order(wrapper_transactions::dsl::block_height.desc()) .select((transaction_history::all_columns, inner_transactions::all_columns, wrapper_transactions::dsl::block_height)) .paginate(page) diff --git a/webserver/src/service/transaction.rs b/webserver/src/service/transaction.rs index 1a20a6ddf..46350e901 100644 --- a/webserver/src/service/transaction.rs +++ b/webserver/src/service/transaction.rs @@ -6,8 +6,9 @@ use crate::repository::tranasaction::{ TransactionRepository, TransactionRepositoryTrait, }; use crate::response::transaction::{ - InnerTransaction, TransactionHistory, WrapperTransaction, + InnerTransaction, TransactionHistory, TransactionKind, WrapperTransaction, }; +use orm::transactions::TransactionKindDb; #[derive(Clone)] pub struct TransactionService { @@ -75,14 +76,87 @@ impl TransactionService { Ok(inner_txs.into_iter().map(InnerTransaction::from).collect()) } + // Helper function to parse transaction types from comma-separated string + fn parse_transaction_types( + transaction_types: Option, + ) -> Option> { + transaction_types.map(|types_str| { + types_str + .split(',') + .filter_map(|type_str| { + let type_str = type_str.trim(); + match type_str { + "transparentTransfer" => { + Some(TransactionKindDb::TransparentTransfer) + } + "shieldedTransfer" => { + Some(TransactionKindDb::ShieldedTransfer) + } + "shieldingTransfer" => { + Some(TransactionKindDb::ShieldingTransfer) + } + "unshieldingTransfer" => { + Some(TransactionKindDb::UnshieldingTransfer) + } + "mixedTransfer" => { + Some(TransactionKindDb::MixedTransfer) + } + "ibcMsgTransfer" => { + Some(TransactionKindDb::IbcMsgTransfer) + } + "ibcTransparentTransfer" => { + Some(TransactionKindDb::IbcTransparentTransfer) + } + "ibcShieldingTransfer" => { + Some(TransactionKindDb::IbcShieldingTransfer) + } + "ibcUnshieldingTransfer" => { + Some(TransactionKindDb::IbcUnshieldingTransfer) + } + "bond" => Some(TransactionKindDb::Bond), + "redelegation" => Some(TransactionKindDb::Redelegation), + "unbond" => Some(TransactionKindDb::Unbond), + "withdraw" => Some(TransactionKindDb::Withdraw), + "claimRewards" => Some(TransactionKindDb::ClaimRewards), + "voteProposal" => Some(TransactionKindDb::VoteProposal), + "initProposal" => Some(TransactionKindDb::InitProposal), + "changeMetadata" => { + Some(TransactionKindDb::ChangeMetadata) + } + "changeCommission" => { + Some(TransactionKindDb::ChangeCommission) + } + "revealPk" => Some(TransactionKindDb::RevealPk), + "becomeValidator" => { + Some(TransactionKindDb::BecomeValidator) + } + "reactivateValidator" => { + Some(TransactionKindDb::ReactivateValidator) + } + "deactivateValidator" => { + Some(TransactionKindDb::DeactivateValidator) + } + "unjailValidator" => { + Some(TransactionKindDb::UnjailValidator) + } + _ => None, // Skip invalid types + } + }) + .collect() + }) + } + pub async fn get_addresses_history( &self, addresses: Vec, page: u64, + transaction_types: Option, ) -> Result<(Vec, u64, u64), TransactionError> { + let parsed_types = Self::parse_transaction_types(transaction_types); + let (txs, total_pages, total_items) = self .transaction_repo - .find_addresses_history(addresses, page as i64) + .find_addresses_history(addresses, page as i64, parsed_types) .await .map_err(TransactionError::Database)?; From 70c24d80c63215ad09a9442adaa868ee29801acd Mon Sep 17 00:00:00 2001 From: neocybereth Date: Fri, 2 May 2025 13:57:38 +1200 Subject: [PATCH 2/6] fix: lint --- webserver/src/service/transaction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webserver/src/service/transaction.rs b/webserver/src/service/transaction.rs index 46350e901..44ae2057a 100644 --- a/webserver/src/service/transaction.rs +++ b/webserver/src/service/transaction.rs @@ -6,7 +6,7 @@ use crate::repository::tranasaction::{ TransactionRepository, TransactionRepositoryTrait, }; use crate::response::transaction::{ - InnerTransaction, TransactionHistory, TransactionKind, WrapperTransaction, + InnerTransaction, TransactionHistory, WrapperTransaction, }; use orm::transactions::TransactionKindDb; From 7d5912b4993004ac862e735714e7c3b8d4b11b03 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Fri, 2 May 2025 14:27:02 +1200 Subject: [PATCH 3/6] fix: remove whitespace --- webserver/src/repository/tranasaction.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/webserver/src/repository/tranasaction.rs b/webserver/src/repository/tranasaction.rs index c33179180..917b7022a 100644 --- a/webserver/src/repository/tranasaction.rs +++ b/webserver/src/repository/tranasaction.rs @@ -8,7 +8,6 @@ use orm::schema::{ use orm::transactions::{ InnerTransactionDb, TransactionHistoryDb, TransactionKindDb, WrapperTransactionDb, }; - use super::utils::{Paginate, PaginatedResponseDb}; use crate::appstate::AppState; From 3cc2d447adf44744851900d1ba5a961e0aa94bea Mon Sep 17 00:00:00 2001 From: neocybereth Date: Fri, 2 May 2025 14:37:18 +1200 Subject: [PATCH 4/6] fix: format --- webserver/src/repository/tranasaction.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/webserver/src/repository/tranasaction.rs b/webserver/src/repository/tranasaction.rs index 917b7022a..6f6b09896 100644 --- a/webserver/src/repository/tranasaction.rs +++ b/webserver/src/repository/tranasaction.rs @@ -1,3 +1,5 @@ +use super::utils::{Paginate, PaginatedResponseDb}; +use crate::appstate::AppState; use axum::async_trait; use diesel::{ ExpressionMethods, JoinOnDsl, QueryDsl, RunQueryDsl, SelectableHelper, @@ -6,10 +8,9 @@ use orm::schema::{ inner_transactions, transaction_history, wrapper_transactions, }; use orm::transactions::{ - InnerTransactionDb, TransactionHistoryDb, TransactionKindDb, WrapperTransactionDb, + InnerTransactionDb, TransactionHistoryDb, TransactionKindDb, + WrapperTransactionDb, }; -use super::utils::{Paginate, PaginatedResponseDb}; -use crate::appstate::AppState; #[derive(Clone)] pub struct TransactionRepository { From 388b4ed3f482d8707fcf237729e18b1ad83d1c92 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Fri, 2 May 2025 15:04:44 +1200 Subject: [PATCH 5/6] fix: formatting --- webserver/src/repository/tranasaction.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/webserver/src/repository/tranasaction.rs b/webserver/src/repository/tranasaction.rs index 6f6b09896..883e5881f 100644 --- a/webserver/src/repository/tranasaction.rs +++ b/webserver/src/repository/tranasaction.rs @@ -122,7 +122,6 @@ impl TransactionRepositoryTrait for TransactionRepository { .inner_join(inner_transactions::table.on(transaction_history::dsl::inner_tx_id.eq(inner_transactions::dsl::id))) .inner_join(wrapper_transactions::table.on(inner_transactions::dsl::wrapper_id.eq(wrapper_transactions::dsl::id))) .into_boxed(); - // Apply transaction type filter if provided if let Some(types) = transaction_types { if !types.is_empty() { From 66a3f6e68ef08e0678c6b80ef51daf6733a7f1a7 Mon Sep 17 00:00:00 2001 From: Gianmarco Fraccaroli Date: Tue, 3 Jun 2025 16:21:33 +0200 Subject: [PATCH 6/6] minors --- orm/src/transactions.rs | 11 +- webserver/src/dto/transaction.rs | 6 +- webserver/src/repository/tranasaction.rs | 11 +- webserver/src/service/transaction.rs | 134 +++++++++++------------ 4 files changed, 85 insertions(+), 77 deletions(-) diff --git a/orm/src/transactions.rs b/orm/src/transactions.rs index 0c5a8fc77..3e2740695 100644 --- a/orm/src/transactions.rs +++ b/orm/src/transactions.rs @@ -9,7 +9,16 @@ use crate::schema::{ inner_transactions, transaction_history, wrapper_transactions, }; -#[derive(Debug, Clone, Serialize, Deserialize, diesel_derive_enum::DbEnum)] +#[derive( + Debug, + Clone, + Serialize, + Deserialize, + diesel_derive_enum::DbEnum, + Eq, + PartialEq, + Hash, +)] #[ExistingTypePath = "crate::schema::sql_types::TransactionKind"] pub enum TransactionKindDb { TransparentTransfer, diff --git a/webserver/src/dto/transaction.rs b/webserver/src/dto/transaction.rs index 4d2b3230f..1fac11139 100644 --- a/webserver/src/dto/transaction.rs +++ b/webserver/src/dto/transaction.rs @@ -1,3 +1,5 @@ +use std::collections::HashSet; + use serde::{Deserialize, Serialize}; use validator::Validate; @@ -8,6 +10,6 @@ pub struct TransactionHistoryQueryParams { pub page: Option, #[validate(length(min = 1, max = 10))] pub addresses: Vec, - // Optional comma-delimited string of transaction types to filter by - pub transaction_types: Option, + #[validate(length(min = 1, max = 20))] + pub transaction_types: Option>, } diff --git a/webserver/src/repository/tranasaction.rs b/webserver/src/repository/tranasaction.rs index 883e5881f..b80f9b66b 100644 --- a/webserver/src/repository/tranasaction.rs +++ b/webserver/src/repository/tranasaction.rs @@ -1,5 +1,5 @@ -use super::utils::{Paginate, PaginatedResponseDb}; -use crate::appstate::AppState; +use std::collections::HashSet; + use axum::async_trait; use diesel::{ ExpressionMethods, JoinOnDsl, QueryDsl, RunQueryDsl, SelectableHelper, @@ -12,6 +12,9 @@ use orm::transactions::{ WrapperTransactionDb, }; +use super::utils::{Paginate, PaginatedResponseDb}; +use crate::appstate::AppState; + #[derive(Clone)] pub struct TransactionRepository { pub(crate) app_state: AppState, @@ -37,7 +40,7 @@ pub trait TransactionRepositoryTrait { &self, addresses: Vec, page: i64, - transaction_types: Option>, + transaction_types: Option>, ) -> Result< PaginatedResponseDb<(TransactionHistoryDb, InnerTransactionDb, i32)>, String, @@ -109,7 +112,7 @@ impl TransactionRepositoryTrait for TransactionRepository { &self, addresses: Vec, page: i64, - transaction_types: Option>, + transaction_types: Option>, ) -> Result< PaginatedResponseDb<(TransactionHistoryDb, InnerTransactionDb, i32)>, String, diff --git a/webserver/src/service/transaction.rs b/webserver/src/service/transaction.rs index 44ae2057a..19eb55d09 100644 --- a/webserver/src/service/transaction.rs +++ b/webserver/src/service/transaction.rs @@ -1,4 +1,6 @@ -use orm::transactions::WrapperTransactionDb; +use std::collections::HashSet; + +use orm::transactions::{TransactionKindDb, WrapperTransactionDb}; use crate::appstate::AppState; use crate::error::transaction::TransactionError; @@ -8,7 +10,6 @@ use crate::repository::tranasaction::{ use crate::response::transaction::{ InnerTransaction, TransactionHistory, WrapperTransaction, }; -use orm::transactions::TransactionKindDb; #[derive(Clone)] pub struct TransactionService { @@ -78,85 +79,78 @@ impl TransactionService { // Helper function to parse transaction types from comma-separated string fn parse_transaction_types( - transaction_types: Option, - ) -> Option> { - transaction_types.map(|types_str| { - types_str - .split(',') - .filter_map(|type_str| { - let type_str = type_str.trim(); - match type_str { - "transparentTransfer" => { - Some(TransactionKindDb::TransparentTransfer) - } - "shieldedTransfer" => { - Some(TransactionKindDb::ShieldedTransfer) - } - "shieldingTransfer" => { - Some(TransactionKindDb::ShieldingTransfer) - } - "unshieldingTransfer" => { - Some(TransactionKindDb::UnshieldingTransfer) - } - "mixedTransfer" => { - Some(TransactionKindDb::MixedTransfer) - } - "ibcMsgTransfer" => { - Some(TransactionKindDb::IbcMsgTransfer) - } - "ibcTransparentTransfer" => { - Some(TransactionKindDb::IbcTransparentTransfer) - } - "ibcShieldingTransfer" => { - Some(TransactionKindDb::IbcShieldingTransfer) - } - "ibcUnshieldingTransfer" => { - Some(TransactionKindDb::IbcUnshieldingTransfer) - } - "bond" => Some(TransactionKindDb::Bond), - "redelegation" => Some(TransactionKindDb::Redelegation), - "unbond" => Some(TransactionKindDb::Unbond), - "withdraw" => Some(TransactionKindDb::Withdraw), - "claimRewards" => Some(TransactionKindDb::ClaimRewards), - "voteProposal" => Some(TransactionKindDb::VoteProposal), - "initProposal" => Some(TransactionKindDb::InitProposal), - "changeMetadata" => { - Some(TransactionKindDb::ChangeMetadata) - } - "changeCommission" => { - Some(TransactionKindDb::ChangeCommission) - } - "revealPk" => Some(TransactionKindDb::RevealPk), - "becomeValidator" => { - Some(TransactionKindDb::BecomeValidator) - } - "reactivateValidator" => { - Some(TransactionKindDb::ReactivateValidator) - } - "deactivateValidator" => { - Some(TransactionKindDb::DeactivateValidator) - } - "unjailValidator" => { - Some(TransactionKindDb::UnjailValidator) - } - _ => None, // Skip invalid types + transaction_types: HashSet, + ) -> HashSet { + transaction_types + .iter() + .filter_map(|type_str| { + let type_str = type_str.trim(); + match type_str { + "transparentTransfer" => { + Some(TransactionKindDb::TransparentTransfer) + } + "shieldedTransfer" => { + Some(TransactionKindDb::ShieldedTransfer) + } + "shieldingTransfer" => { + Some(TransactionKindDb::ShieldingTransfer) + } + "unshieldingTransfer" => { + Some(TransactionKindDb::UnshieldingTransfer) + } + "mixedTransfer" => Some(TransactionKindDb::MixedTransfer), + "ibcMsgTransfer" => Some(TransactionKindDb::IbcMsgTransfer), + "ibcTransparentTransfer" => { + Some(TransactionKindDb::IbcTransparentTransfer) + } + "ibcShieldingTransfer" => { + Some(TransactionKindDb::IbcShieldingTransfer) + } + "ibcUnshieldingTransfer" => { + Some(TransactionKindDb::IbcUnshieldingTransfer) + } + "bond" => Some(TransactionKindDb::Bond), + "redelegation" => Some(TransactionKindDb::Redelegation), + "unbond" => Some(TransactionKindDb::Unbond), + "withdraw" => Some(TransactionKindDb::Withdraw), + "claimRewards" => Some(TransactionKindDb::ClaimRewards), + "voteProposal" => Some(TransactionKindDb::VoteProposal), + "initProposal" => Some(TransactionKindDb::InitProposal), + "changeMetadata" => Some(TransactionKindDb::ChangeMetadata), + "changeCommission" => { + Some(TransactionKindDb::ChangeCommission) + } + "revealPk" => Some(TransactionKindDb::RevealPk), + "becomeValidator" => { + Some(TransactionKindDb::BecomeValidator) + } + "reactivateValidator" => { + Some(TransactionKindDb::ReactivateValidator) + } + "deactivateValidator" => { + Some(TransactionKindDb::DeactivateValidator) + } + "unjailValidator" => { + Some(TransactionKindDb::UnjailValidator) } - }) - .collect() - }) + _ => None, // Skip invalid types + } + }) + .collect::>() } pub async fn get_addresses_history( &self, addresses: Vec, page: u64, - transaction_types: Option, + transaction_types: Option>, ) -> Result<(Vec, u64, u64), TransactionError> { - let parsed_types = Self::parse_transaction_types(transaction_types); + let transaction_types = + transaction_types.map(Self::parse_transaction_types); let (txs, total_pages, total_items) = self .transaction_repo - .find_addresses_history(addresses, page as i64, parsed_types) + .find_addresses_history(addresses, page as i64, transaction_types) .await .map_err(TransactionError::Database)?;