openzeppelin_relayer/api/controllers/
relayer.rs1use crate::{
10 domain::{
11 get_network_relayer, get_network_relayer_by_model, get_relayer_by_id,
12 get_relayer_transaction_by_model, get_transaction_by_id as get_tx_by_id, Relayer,
13 RelayerUpdateRequest, SignDataRequest, SignDataResponse, SignTypedDataRequest, Transaction,
14 },
15 jobs::JobProducer,
16 models::{
17 convert_to_internal_rpc_request, ApiError, ApiResponse, AppState,
18 NetworkTransactionRequest, NetworkType, PaginationMeta, PaginationQuery, RelayerResponse,
19 TransactionResponse,
20 },
21 repositories::{RelayerRepository, Repository, TransactionRepository},
22};
23use actix_web::{web, HttpResponse};
24use eyre::Result;
25
26pub async fn list_relayers(
37 query: PaginationQuery,
38 state: web::ThinData<AppState<JobProducer>>,
39) -> Result<HttpResponse, ApiError> {
40 let relayers = state.relayer_repository.list_paginated(query).await?;
41
42 let mapped_relayers: Vec<RelayerResponse> =
43 relayers.items.into_iter().map(|r| r.into()).collect();
44
45 Ok(HttpResponse::Ok().json(ApiResponse::paginated(
46 mapped_relayers,
47 PaginationMeta {
48 total_items: relayers.total,
49 current_page: relayers.page,
50 per_page: relayers.per_page,
51 },
52 )))
53}
54
55pub async fn get_relayer(
66 relayer_id: String,
67 state: web::ThinData<AppState<JobProducer>>,
68) -> Result<HttpResponse, ApiError> {
69 let relayer = get_relayer_by_id(relayer_id, &state).await?;
70
71 let relayer_response: RelayerResponse = relayer.into();
72
73 Ok(HttpResponse::Ok().json(ApiResponse::success(relayer_response)))
74}
75
76pub async fn update_relayer(
88 relayer_id: String,
89 update_req: RelayerUpdateRequest,
90 state: web::ThinData<AppState<JobProducer>>,
91) -> Result<HttpResponse, ApiError> {
92 let relayer = get_relayer_by_id(relayer_id.clone(), &state).await?;
93
94 if relayer.system_disabled || (relayer.paused && update_req.paused != Some(false)) {
95 let error_message = if relayer.system_disabled {
96 "Relayer is disabled"
97 } else {
98 "Relayer is paused"
99 };
100 return Err(ApiError::BadRequest(error_message.to_string()));
101 }
102
103 let updated_relayer = state
104 .relayer_repository
105 .partial_update(relayer_id.clone(), update_req)
106 .await?;
107
108 let relayer_response: RelayerResponse = updated_relayer.into();
109
110 Ok(HttpResponse::Ok().json(ApiResponse::success(relayer_response)))
111}
112
113pub async fn get_relayer_status(
124 relayer_id: String,
125 state: web::ThinData<AppState<JobProducer>>,
126) -> Result<HttpResponse, ApiError> {
127 let relayer = get_network_relayer(relayer_id, &state).await?;
128
129 let status = relayer.get_status().await?;
130
131 Ok(HttpResponse::Ok().json(ApiResponse::success(status)))
132}
133
134pub async fn get_relayer_balance(
145 relayer_id: String,
146 state: web::ThinData<AppState<JobProducer>>,
147) -> Result<HttpResponse, ApiError> {
148 let relayer = get_network_relayer(relayer_id, &state).await?;
149
150 let result = relayer.get_balance().await?;
151
152 Ok(HttpResponse::Ok().json(ApiResponse::success(result)))
153}
154
155pub async fn send_transaction(
167 relayer_id: String,
168 request: serde_json::Value,
169 state: web::ThinData<AppState<JobProducer>>,
170) -> Result<HttpResponse, ApiError> {
171 let relayer_repo_model = get_relayer_by_id(relayer_id, &state).await?;
172 relayer_repo_model.validate_active_state()?;
173
174 let relayer = get_network_relayer(relayer_repo_model.id.clone(), &state).await?;
175
176 let tx_request: NetworkTransactionRequest =
177 NetworkTransactionRequest::from_json(&relayer_repo_model.network_type, request.clone())?;
178
179 tx_request.validate(&relayer_repo_model)?;
180
181 let transaction = relayer.process_transaction_request(tx_request).await?;
182
183 let transaction_response: TransactionResponse = transaction.into();
184
185 Ok(HttpResponse::Ok().json(ApiResponse::success(transaction_response)))
186}
187
188pub async fn get_transaction_by_id(
200 relayer_id: String,
201 transaction_id: String,
202 state: web::ThinData<AppState<JobProducer>>,
203) -> Result<HttpResponse, ApiError> {
204 if relayer_id.is_empty() || transaction_id.is_empty() {
205 return Ok(HttpResponse::Ok().json(ApiResponse::<()>::error(
206 "Invalid relayer or transaction ID".to_string(),
207 )));
208 }
209 get_relayer_by_id(relayer_id, &state).await?;
211
212 let transaction = get_tx_by_id(transaction_id, &state).await?;
213
214 let transaction_response: TransactionResponse = transaction.into();
215
216 Ok(HttpResponse::Ok().json(ApiResponse::success(transaction_response)))
217}
218
219pub async fn get_transaction_by_nonce(
231 relayer_id: String,
232 nonce: u64,
233 state: web::ThinData<AppState<JobProducer>>,
234) -> Result<HttpResponse, ApiError> {
235 let relayer = get_relayer_by_id(relayer_id.clone(), &state).await?;
236
237 if relayer.network_type != NetworkType::Evm {
239 return Err(ApiError::NotSupported(
240 "Nonce lookup only supported for EVM networks".into(),
241 ));
242 }
243
244 let transaction = state
245 .transaction_repository
246 .find_by_nonce(&relayer_id, nonce)
247 .await?
248 .ok_or_else(|| ApiError::NotFound(format!("Transaction with nonce {} not found", nonce)))?;
249
250 let transaction_response: TransactionResponse = transaction.into();
251
252 Ok(HttpResponse::Ok().json(ApiResponse::success(transaction_response)))
253}
254
255pub async fn list_transactions(
267 relayer_id: String,
268 query: PaginationQuery,
269 state: web::ThinData<AppState<JobProducer>>,
270) -> Result<HttpResponse, ApiError> {
271 get_relayer_by_id(relayer_id.clone(), &state).await?;
272
273 let transactions = state
274 .transaction_repository
275 .find_by_relayer_id(&relayer_id, query)
276 .await?;
277
278 let transaction_response_list: Vec<TransactionResponse> =
279 transactions.items.into_iter().map(|t| t.into()).collect();
280
281 Ok(HttpResponse::Ok().json(ApiResponse::paginated(
282 transaction_response_list,
283 PaginationMeta {
284 total_items: transactions.total,
285 current_page: transactions.page,
286 per_page: transactions.per_page,
287 },
288 )))
289}
290
291pub async fn delete_pending_transactions(
302 relayer_id: String,
303 state: web::ThinData<AppState<JobProducer>>,
304) -> Result<HttpResponse, ApiError> {
305 let relayer = get_relayer_by_id(relayer_id, &state).await?;
306 relayer.validate_active_state()?;
307 let network_relayer = get_network_relayer_by_model(relayer.clone(), &state).await?;
308
309 let result = network_relayer.delete_pending_transactions().await?;
310
311 Ok(HttpResponse::Ok().json(ApiResponse::success(result)))
312}
313
314pub async fn cancel_transaction(
326 relayer_id: String,
327 transaction_id: String,
328 state: web::ThinData<AppState<JobProducer>>,
329) -> Result<HttpResponse, ApiError> {
330 let relayer = get_relayer_by_id(relayer_id.clone(), &state).await?;
331 relayer.validate_active_state()?;
332
333 let relayer_transaction = get_relayer_transaction_by_model(relayer.clone(), &state).await?;
334
335 let transaction_to_cancel = get_tx_by_id(transaction_id, &state).await?;
336
337 let canceled_transaction = relayer_transaction
338 .cancel_transaction(transaction_to_cancel)
339 .await?;
340
341 let transaction_response: TransactionResponse = canceled_transaction.into();
342
343 Ok(HttpResponse::Ok().json(ApiResponse::success(transaction_response)))
344}
345
346pub async fn replace_transaction(
359 relayer_id: String,
360 transaction_id: String,
361 request: serde_json::Value,
362 state: web::ThinData<AppState<JobProducer>>,
363) -> Result<HttpResponse, ApiError> {
364 let relayer = get_relayer_by_id(relayer_id.clone(), &state).await?;
365 relayer.validate_active_state()?;
366
367 let new_tx_request: NetworkTransactionRequest =
368 NetworkTransactionRequest::from_json(&relayer.network_type, request.clone())?;
369 new_tx_request.validate(&relayer)?;
370
371 let transaction_to_replace = state
372 .transaction_repository
373 .get_by_id(transaction_id)
374 .await?;
375
376 let relayer_transaction = get_relayer_transaction_by_model(relayer.clone(), &state).await?;
377 let replaced_transaction = relayer_transaction
378 .replace_transaction(transaction_to_replace, new_tx_request)
379 .await?;
380
381 Ok(HttpResponse::Ok().json(ApiResponse::success(replaced_transaction)))
382}
383
384pub async fn sign_data(
396 relayer_id: String,
397 request: SignDataRequest,
398 state: web::ThinData<AppState<JobProducer>>,
399) -> Result<HttpResponse, ApiError> {
400 let relayer = get_relayer_by_id(relayer_id.clone(), &state).await?;
401 relayer.validate_active_state()?;
402 let network_relayer = get_network_relayer_by_model(relayer, &state).await?;
403
404 let result = network_relayer.sign_data(request).await?;
405
406 if let SignDataResponse::Evm(sign) = result {
407 Ok(HttpResponse::Ok().json(ApiResponse::success(sign)))
408 } else {
409 Err(ApiError::NotSupported("Sign data not supported".into()))
410 }
411}
412
413pub async fn sign_typed_data(
425 relayer_id: String,
426 request: SignTypedDataRequest,
427 state: web::ThinData<AppState<JobProducer>>,
428) -> Result<HttpResponse, ApiError> {
429 let relayer = get_relayer_by_id(relayer_id.clone(), &state).await?;
430 relayer.validate_active_state()?;
431 let network_relayer = get_network_relayer_by_model(relayer, &state).await?;
432
433 let result = network_relayer.sign_typed_data(request).await?;
434
435 Ok(HttpResponse::Ok().json(ApiResponse::success(result)))
436}
437
438pub async fn relayer_rpc(
450 relayer_id: String,
451 request: serde_json::Value,
452 state: web::ThinData<AppState<JobProducer>>,
453) -> Result<HttpResponse, ApiError> {
454 let relayer = get_relayer_by_id(relayer_id.clone(), &state).await?;
455 relayer.validate_active_state()?;
456 let network_relayer = get_network_relayer_by_model(relayer.clone(), &state).await?;
457
458 let internal_request = convert_to_internal_rpc_request(request, &relayer.network_type)?;
459 let result = network_relayer.rpc(internal_request).await?;
460
461 Ok(HttpResponse::Ok().json(result))
462}