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