1use crate::cli::tx;
4use crate::web::AppState;
5use axum::Json;
6use axum::extract::State;
7use axum::http::StatusCode;
8use axum::response::IntoResponse;
9use serde::Deserialize;
10use std::sync::Arc;
11
12#[derive(Debug, Deserialize)]
14pub struct TxRequest {
15 pub hash: String,
17 #[serde(default = "default_chain")]
19 pub chain: String,
20 #[serde(default)]
22 pub decode: bool,
23 #[serde(default)]
25 pub trace: bool,
26}
27
28fn default_chain() -> String {
29 "ethereum".to_string()
30}
31
32pub async fn handle(
34 State(state): State<Arc<AppState>>,
35 Json(req): Json<TxRequest>,
36) -> impl IntoResponse {
37 match tx::fetch_transaction_report(&req.hash, &req.chain, req.decode, req.trace, &state.factory)
38 .await
39 {
40 Ok(report) => Json(serde_json::json!(report)).into_response(),
41 Err(e) => (
42 StatusCode::INTERNAL_SERVER_ERROR,
43 Json(serde_json::json!({ "error": e.to_string() })),
44 )
45 .into_response(),
46 }
47}
48
49#[cfg(test)]
50mod tests {
51 use super::*;
52
53 #[test]
54 fn test_deserialize_full() {
55 let json = serde_json::json!({
56 "hash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
57 "chain": "polygon",
58 "decode": true,
59 "trace": true
60 });
61 let req: TxRequest = serde_json::from_value(json).unwrap();
62 assert_eq!(
63 req.hash,
64 "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
65 );
66 assert_eq!(req.chain, "polygon");
67 assert!(req.decode);
68 assert!(req.trace);
69 }
70
71 #[test]
72 fn test_deserialize_minimal() {
73 let json = serde_json::json!({
74 "hash": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
75 });
76 let req: TxRequest = serde_json::from_value(json).unwrap();
77 assert_eq!(
78 req.hash,
79 "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
80 );
81 assert_eq!(req.chain, "ethereum");
82 assert!(!req.decode);
83 assert!(!req.trace);
84 }
85
86 #[test]
87 fn test_defaults() {
88 assert_eq!(default_chain(), "ethereum");
89 }
90
91 #[test]
92 fn test_decode_trace_flags() {
93 let json_decode = serde_json::json!({
94 "hash": "0x123",
95 "decode": true,
96 "trace": false
97 });
98 let req_decode: TxRequest = serde_json::from_value(json_decode).unwrap();
99 assert!(req_decode.decode);
100 assert!(!req_decode.trace);
101
102 let json_trace = serde_json::json!({
103 "hash": "0x123",
104 "decode": false,
105 "trace": true
106 });
107 let req_trace: TxRequest = serde_json::from_value(json_trace).unwrap();
108 assert!(!req_trace.decode);
109 assert!(req_trace.trace);
110 }
111
112 #[tokio::test]
113 async fn test_handle_tx_direct() {
114 use crate::chains::DefaultClientFactory;
115 use crate::config::Config;
116 use crate::web::AppState;
117 use axum::extract::State;
118 use axum::response::IntoResponse;
119
120 let config = Config::default();
121 let factory = DefaultClientFactory {
122 chains_config: config.chains.clone(),
123 };
124 let state = std::sync::Arc::new(AppState { config, factory });
125 let req = TxRequest {
126 hash: "0xabc123def456789012345678901234567890123456789012345678901234abcd".to_string(),
127 chain: "ethereum".to_string(),
128 decode: false,
129 trace: false,
130 };
131 let response = handle(State(state), axum::Json(req)).await.into_response();
132 let status = response.status();
133 assert!(status.is_success() || status.is_client_error() || status.is_server_error());
134 }
135}