Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Api refactor #14

Merged
merged 11 commits into from
Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this creates a new opportunity? If so, I think it should be a POST to /v1/liquidation/opportunities to match the usual REST conventions.

)
.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
Loading