openzeppelin_relayer/api/routes/
relayer.rsuse crate::{
api::controllers::relayer,
domain::{JsonRpcRequest, RelayerUpdateRequest, SignDataRequest, SignTypedDataRequest},
jobs::JobProducer,
models::{AppState, NetworkRpcRequest, PaginationQuery},
};
use actix_web::{delete, get, patch, post, put, web, Responder};
use serde::Deserialize;
use utoipa::ToSchema;
#[get("/relayers")]
async fn list_relayers(
query: web::Query<PaginationQuery>,
data: web::ThinData<AppState<JobProducer>>,
) -> impl Responder {
relayer::list_relayers(query.into_inner(), data).await
}
#[get("/relayers/{relayer_id}")]
async fn get_relayer(
relayer_id: web::Path<String>,
data: web::ThinData<AppState<JobProducer>>,
) -> impl Responder {
relayer::get_relayer(relayer_id.into_inner(), data).await
}
#[patch("/relayers/{relayer_id}")]
async fn update_relayer(
relayer_id: web::Path<String>,
update_req: web::Json<RelayerUpdateRequest>,
data: web::ThinData<AppState<JobProducer>>,
) -> impl Responder {
relayer::update_relayer(relayer_id.into_inner(), update_req.into_inner(), data).await
}
#[get("/relayers/{relayer_id}/status")]
async fn get_relayer_status(
relayer_id: web::Path<String>,
data: web::ThinData<AppState<JobProducer>>,
) -> impl Responder {
relayer::get_relayer_status(relayer_id.into_inner(), data).await
}
#[get("/relayers/{relayer_id}/balance")]
async fn get_relayer_balance(
relayer_id: web::Path<String>,
data: web::ThinData<AppState<JobProducer>>,
) -> impl Responder {
relayer::get_relayer_balance(relayer_id.into_inner(), data).await
}
#[post("/relayers/{relayer_id}/transactions")]
async fn send_transaction(
relayer_id: web::Path<String>,
req: web::Json<serde_json::Value>,
data: web::ThinData<AppState<JobProducer>>,
) -> impl Responder {
relayer::send_transaction(relayer_id.into_inner(), req.into_inner(), data).await
}
#[derive(Deserialize, ToSchema)]
pub struct TransactionPath {
relayer_id: String,
transaction_id: String,
}
#[get("/relayers/{relayer_id}/transactions/{transaction_id}")]
async fn get_transaction_by_id(
path: web::Path<TransactionPath>,
data: web::ThinData<AppState<JobProducer>>,
) -> impl Responder {
let path = path.into_inner();
relayer::get_transaction_by_id(path.relayer_id, path.transaction_id, data).await
}
#[get("/relayers/{relayer_id}/transactions/by-nonce/{nonce}")]
async fn get_transaction_by_nonce(
params: web::Path<(String, u64)>,
data: web::ThinData<AppState<JobProducer>>,
) -> impl Responder {
let params = params.into_inner();
relayer::get_transaction_by_nonce(params.0, params.1, data).await
}
#[get("/relayers/{relayer_id}/transactions")]
async fn list_transactions(
relayer_id: web::Path<String>,
query: web::Query<PaginationQuery>,
data: web::ThinData<AppState<JobProducer>>,
) -> impl Responder {
relayer::list_transactions(relayer_id.into_inner(), query.into_inner(), data).await
}
#[delete("/relayers/{relayer_id}/transactions/pending")]
async fn delete_pending_transactions(
relayer_id: web::Path<String>,
data: web::ThinData<AppState<JobProducer>>,
) -> impl Responder {
relayer::delete_pending_transactions(relayer_id.into_inner(), data).await
}
#[delete("/relayers/{relayer_id}/transactions/{transaction_id}")]
async fn cancel_transaction(
path: web::Path<TransactionPath>,
data: web::ThinData<AppState<JobProducer>>,
) -> impl Responder {
let path = path.into_inner();
relayer::cancel_transaction(path.relayer_id, path.transaction_id, data).await
}
#[put("/relayers/{relayer_id}/transactions/{transaction_id}")]
async fn replace_transaction(
path: web::Path<TransactionPath>,
data: web::ThinData<AppState<JobProducer>>,
) -> impl Responder {
let path = path.into_inner();
relayer::replace_transaction(path.relayer_id, path.transaction_id, data).await
}
#[post("/relayers/{relayer_id}/sign")]
async fn sign(
relayer_id: web::Path<String>,
req: web::Json<SignDataRequest>,
data: web::ThinData<AppState<JobProducer>>,
) -> impl Responder {
relayer::sign_data(relayer_id.into_inner(), req.into_inner(), data).await
}
#[post("/relayers/{relayer_id}/sign-typed-data")]
async fn sign_typed_data(
relayer_id: web::Path<String>,
req: web::Json<SignTypedDataRequest>,
data: web::ThinData<AppState<JobProducer>>,
) -> impl Responder {
relayer::sign_typed_data(relayer_id.into_inner(), req.into_inner(), data).await
}
#[post("/relayers/{relayer_id}/rpc")]
async fn rpc(
relayer_id: web::Path<String>,
req: web::Json<JsonRpcRequest<NetworkRpcRequest>>,
data: web::ThinData<AppState<JobProducer>>,
) -> impl Responder {
relayer::relayer_rpc(relayer_id.into_inner(), req.into_inner(), data).await
}
pub fn init(cfg: &mut web::ServiceConfig) {
cfg.service(delete_pending_transactions); cfg.service(cancel_transaction); cfg.service(replace_transaction); cfg.service(get_transaction_by_id); cfg.service(get_transaction_by_nonce); cfg.service(send_transaction); cfg.service(list_transactions); cfg.service(get_relayer_status); cfg.service(get_relayer_balance); cfg.service(sign); cfg.service(sign_typed_data); cfg.service(rpc); cfg.service(get_relayer); cfg.service(update_relayer); cfg.service(list_relayers); }
#[cfg(test)]
mod tests {
use super::*;
use crate::{
jobs::{JobProducer, Queue},
repositories::{
InMemoryNotificationRepository, InMemoryRelayerRepository, InMemorySignerRepository,
InMemoryTransactionCounter, InMemoryTransactionRepository, RelayerRepositoryStorage,
},
};
use actix_web::{http::StatusCode, test, App};
use std::sync::Arc;
async fn get_test_app_state() -> AppState<JobProducer> {
AppState {
relayer_repository: Arc::new(RelayerRepositoryStorage::in_memory(
InMemoryRelayerRepository::new(),
)),
transaction_repository: Arc::new(InMemoryTransactionRepository::new()),
signer_repository: Arc::new(InMemorySignerRepository::new()),
notification_repository: Arc::new(InMemoryNotificationRepository::new()),
transaction_counter_store: Arc::new(InMemoryTransactionCounter::new()),
job_producer: Arc::new(JobProducer::new(Queue::setup().await.unwrap())),
}
}
#[actix_web::test]
async fn test_routes_are_registered() -> Result<(), color_eyre::eyre::Error> {
let app = test::init_service(
App::new()
.app_data(web::Data::new(get_test_app_state()))
.configure(init),
)
.await;
let req = test::TestRequest::get().uri("/relayers").to_request();
let resp = test::call_service(&app, req).await;
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
let req = test::TestRequest::get()
.uri("/relayers/test-id")
.to_request();
let resp = test::call_service(&app, req).await;
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
let req = test::TestRequest::patch()
.uri("/relayers/test-id")
.set_json(serde_json::json!({}))
.to_request();
let resp = test::call_service(&app, req).await;
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
let req = test::TestRequest::get()
.uri("/relayers/test-id/status")
.to_request();
let resp = test::call_service(&app, req).await;
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
let req = test::TestRequest::get()
.uri("/relayers/test-id/balance")
.to_request();
let resp = test::call_service(&app, req).await;
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
let req = test::TestRequest::post()
.uri("/relayers/test-id/transactions")
.set_json(serde_json::json!({}))
.to_request();
let resp = test::call_service(&app, req).await;
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
let req = test::TestRequest::get()
.uri("/relayers/test-id/transactions/tx-123")
.to_request();
let resp = test::call_service(&app, req).await;
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
let req = test::TestRequest::get()
.uri("/relayers/test-id/transactions/by-nonce/123")
.to_request();
let resp = test::call_service(&app, req).await;
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
let req = test::TestRequest::get()
.uri("/relayers/test-id/transactions")
.to_request();
let resp = test::call_service(&app, req).await;
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
let req = test::TestRequest::delete()
.uri("/relayers/test-id/transactions/pending")
.to_request();
let resp = test::call_service(&app, req).await;
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
let req = test::TestRequest::delete()
.uri("/relayers/test-id/transactions/tx-123")
.to_request();
let resp = test::call_service(&app, req).await;
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
let req = test::TestRequest::put()
.uri("/relayers/test-id/transactions/tx-123")
.to_request();
let resp = test::call_service(&app, req).await;
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
let req = test::TestRequest::post()
.uri("/relayers/test-id/sign")
.set_json(serde_json::json!({
"message": "0x1234567890abcdef"
}))
.to_request();
let resp = test::call_service(&app, req).await;
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
let req = test::TestRequest::post()
.uri("/relayers/test-id/sign-typed-data")
.set_json(serde_json::json!({
"domain_separator": "0x1234567890abcdef",
"hash_struct_message": "0x1234567890abcdef"
}))
.to_request();
let resp = test::call_service(&app, req).await;
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
let req = test::TestRequest::post()
.uri("/relayers/test-id/rpc")
.set_json(serde_json::json!({
"jsonrpc": "2.0",
"method": "eth_getBlockByNumber",
"params": ["0x1", true],
"id": 1
}))
.to_request();
let resp = test::call_service(&app, req).await;
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
Ok(())
}
}