Skip to content

Commit

Permalink
Api refactor (#14)
Browse files Browse the repository at this point in the history
* Rename modules

* Add /v1/ prefix to all paths

* Json error responses

* Add params for fetch opportunities

* Restful endpoint names

* Better naming and structure of liquidation objects + versioning for opportunity

* Make all endpoints return json

* Consistent variable naming
  • Loading branch information
m30m authored Feb 7, 2024
1 parent a5a8b6e commit 57de360
Show file tree
Hide file tree
Showing 10 changed files with 485 additions and 424 deletions.
103 changes: 58 additions & 45 deletions auction-server/src/api.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use {
crate::{
api::{
marketplace::{
LiquidationOpportunity,
bid::{
Bid,
BidResult,
},
liquidation::{
OpportunityBid,
TokenQty,
OpportunityParamsWithId,
},
rest::Bid,
},
auction::run_submission_loop,
config::{
Expand All @@ -18,7 +20,10 @@ use {
state::{
ChainStore,
LiquidationStore,
OpportunityParams,
OpportunityParamsV1,
Store,
TokenQty,
},
},
anyhow::{
Expand All @@ -35,6 +40,7 @@ use {
get,
post,
},
Json,
Router,
},
clap::crate_version,
Expand All @@ -51,6 +57,7 @@ use {
types::Bytes,
},
futures::future::join_all,
serde::Serialize,
std::{
collections::HashMap,
sync::{
Expand Down Expand Up @@ -83,11 +90,9 @@ async fn root() -> String {
format!("PER Auction Server API {}", crate_version!())
}

pub(crate) mod marketplace;
mod rest;
mod bid;
pub(crate) mod liquidation;

#[derive(ToResponse, ToSchema)]
#[response(description = "An error occurred processing the request")]
pub enum RestError {
/// The request contained invalid parameters
BadParameters(String),
Expand All @@ -96,11 +101,7 @@ pub enum RestError {
/// The chain id is not supported
InvalidChainId,
/// The simulation failed
SimulationError {
#[schema(value_type=String)]
result: Bytes,
reason: String,
},
SimulationError { result: Bytes, reason: String },
/// The order was not found
OpportunityNotFound,
/// The server cannot currently communicate with the blockchain, so is not able to verify
Expand All @@ -110,42 +111,44 @@ pub enum RestError {
Unknown,
}

#[derive(ToResponse, ToSchema, Serialize)]
#[response(description = "An error occurred processing the request")]
struct ErrorBodyResponse {
error: String,
}

impl IntoResponse for RestError {
fn into_response(self) -> Response {
match self {
let (status, msg) = match self {
RestError::BadParameters(msg) => {
(StatusCode::BAD_REQUEST, format!("Bad parameters: {}", msg)).into_response()
(StatusCode::BAD_REQUEST, format!("Bad parameters: {}", msg))
}
RestError::InvalidOpportunity(msg) => (
StatusCode::BAD_REQUEST,
format!("Invalid opportunity: {}", msg),
)
.into_response(),
RestError::InvalidChainId => {
(StatusCode::BAD_REQUEST, "The chain id is not supported").into_response()
}
),
RestError::InvalidChainId => (
StatusCode::NOT_FOUND,
"The chain id is not found".to_string(),
),
RestError::SimulationError { result, reason } => (
StatusCode::BAD_REQUEST,
format!("Simulation failed: {} ({})", result, reason),
)
.into_response(),
),
RestError::OpportunityNotFound => (
StatusCode::NOT_FOUND,
"Order with the specified id was not found",
)
.into_response(),

"Opportunity with the specified id was not found".to_string(),
),
RestError::TemporarilyUnavailable => (
StatusCode::SERVICE_UNAVAILABLE,
"This service is temporarily unavailable",
)
.into_response(),
"This service is temporarily unavailable".to_string(),
),
RestError::Unknown => (
StatusCode::INTERNAL_SERVER_ERROR,
"An unknown error occurred processing the request",
)
.into_response(),
}
"An unknown error occurred processing the request".to_string(),
),
};
(status, Json(ErrorBodyResponse { error: msg })).into_response()
}
}

Expand All @@ -160,13 +163,23 @@ pub async fn start_server(run_options: RunOptions) -> Result<()> {
#[derive(OpenApi)]
#[openapi(
paths(
rest::bid,
marketplace::submit_opportunity,
marketplace::bid_opportunity,
marketplace::fetch_opportunities,
bid::bid,
liquidation::post_opportunity,
liquidation::post_bid,
liquidation::get_opportunities,
),
components(
schemas(Bid),schemas(LiquidationOpportunity),schemas(OpportunityBid), schemas(TokenQty),responses(RestError)
schemas(Bid),
schemas(OpportunityParamsV1),
schemas(OpportunityBid),
schemas(OpportunityParams),
schemas(OpportunityParamsWithId),
schemas(TokenQty),
schemas(BidResult),
schemas(ErrorBodyResponse),
responses(ErrorBodyResponse),
responses(OpportunityParamsWithId),
responses(BidResult)
),
tags(
(name = "PER Auction", description = "Pyth Express Relay Auction Server")
Expand Down Expand Up @@ -228,18 +241,18 @@ pub async fn start_server(run_options: RunOptions) -> Result<()> {
let app: Router<()> = Router::new()
.merge(SwaggerUi::new("/docs").url("/docs/openapi.json", ApiDoc::openapi()))
.route("/", get(root))
.route("/bid", post(rest::bid))
.route("/v1/bids", post(bid::bid))
.route(
"/liquidation/submit_opportunity",
post(marketplace::submit_opportunity),
"/v1/liquidation/opportunities",
post(liquidation::post_opportunity),
)
.route(
"/liquidation/fetch_opportunities",
get(marketplace::fetch_opportunities),
"/v1/liquidation/opportunities",
get(liquidation::get_opportunities),
)
.route(
"/liquidation/bid_opportunity",
post(marketplace::bid_opportunity),
"/v1/liquidation/opportunities/:opportunity_id/bids",
post(liquidation::post_bid),
)
.layer(CorsLayer::permissive())
.with_state(server_store);
Expand Down
41 changes: 30 additions & 11 deletions auction-server/src/api/rest.rs → auction-server/src/api/bid.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use {
crate::{
api::RestError,
api::{
ErrorBodyResponse,
RestError,
},
auction::{
simulate_bids,
SimulationError,
Expand Down Expand Up @@ -29,7 +32,10 @@ use {
Serialize,
},
std::sync::Arc,
utoipa::ToSchema,
utoipa::{
ToResponse,
ToSchema,
},
};

#[derive(Serialize, Deserialize, ToSchema, Clone)]
Expand All @@ -38,7 +44,7 @@ pub struct Bid {
#[schema(example = "0xdeadbeef", value_type=String)]
pub permission_key: Bytes,
/// The chain id to bid on.
#[schema(example = "sepolia")]
#[schema(example = "sepolia", value_type=String)]
pub chain_id: String,
/// The contract address to call.
#[schema(example = "0xcA11bde05977b3631167028862bE2a173976CA11",value_type = String)]
Expand All @@ -52,7 +58,7 @@ pub struct Bid {
pub amount: U256,
}

pub async fn handle_bid(store: Arc<Store>, bid: Bid) -> Result<String, RestError> {
pub async fn handle_bid(store: Arc<Store>, bid: Bid) -> Result<(), RestError> {
let chain_store = store
.chains
.get(&bid.chain_id)
Expand Down Expand Up @@ -96,25 +102,38 @@ pub async fn handle_bid(store: Arc<Store>, bid: Bid) -> Result<String, RestError
calldata: bid.calldata.clone(),
bid: bid.amount,
});
Ok("OK".to_string())
Ok(())
}

#[derive(Serialize, Deserialize, ToResponse, ToSchema, Clone)]
pub struct BidResult {
pub status: String,
}

/// Bid on a specific permission key for a specific chain.
///
/// Your bid will be simulated and verified by the server. Depending on the outcome of the auction, a transaction containing the contract call will be sent to the blockchain expecting the bid amount to be paid after the call.
#[utoipa::path(post, path = "/bid", request_body = Bid, responses(
(status = 200, description = "Bid was placed succesfully", body = String),
(status = 400, response=RestError)
/// Your bid will be simulated and verified by the server. Depending on the outcome of the auction, a transaction
/// containing the contract call will be sent to the blockchain expecting the bid amount to be paid after the call.
#[utoipa::path(post, path = "/v1/bids", request_body = Bid, responses(
(status = 200, description = "Bid was placed succesfully", body = BidResult, example = json!({"status": "OK"})),
(status = 400, response = ErrorBodyResponse),
(status = 404, description = "Chain id was not found", body = ErrorBodyResponse),
),)]
pub async fn bid(
State(store): State<Arc<Store>>,
Json(bid): Json<Bid>,
) -> Result<String, RestError> {
) -> Result<Json<BidResult>, RestError> {
let bid = bid.clone();
store
.chains
.get(&bid.chain_id)
.ok_or(RestError::InvalidChainId)?;

handle_bid(store, bid).await
match handle_bid(store, bid).await {
Ok(_) => Ok(BidResult {
status: "OK".to_string(),
}
.into()),
Err(e) => Err(e),
}
}
Loading

0 comments on commit 57de360

Please sign in to comment.