Skip to content

Commit

Permalink
edits_feb5 (#15)
Browse files Browse the repository at this point in the history
* precom changes

* testing changes to accomodate updates

* package changes

* precom fix

* clearer error raising on smart contracts

* price updates in mock pyth format

* address comments

* fix poetry version mismatch

* changes to clones and write locks

* address comments

---------

Co-authored-by: Anirudh Suresh <ani@Anirudhs-MacBook-Pro.local>
  • Loading branch information
anihamde and Anirudh Suresh authored Feb 13, 2024
1 parent 7a53ef1 commit 6da5e4c
Show file tree
Hide file tree
Showing 14 changed files with 810 additions and 84 deletions.
71 changes: 51 additions & 20 deletions auction-server/src/api/liquidation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,15 @@ pub struct OpportunityParamsWithId {
params: OpportunityParams,
}

impl Into<OpportunityParamsWithId> for LiquidationOpportunity {
fn into(self) -> OpportunityParamsWithId {
OpportunityParamsWithId {
opportunity_id: self.id,
params: self.params,
}
}
}

/// Submit a liquidation opportunity ready to be executed.
///
/// The opportunity will be verified by the server. If the opportunity is valid, it will be stored in the database
Expand Down Expand Up @@ -105,12 +114,29 @@ pub async fn post_opportunity(
.await
.map_err(|e| RestError::InvalidOpportunity(e.to_string()))?;

store
.liquidation_store
.opportunities
.write()
.await
.insert(params.permission_key.clone(), opportunity);

let mut write_lock = store.liquidation_store.opportunities.write().await;

if let Some(opportunities_existing) = write_lock.get_mut(&params.permission_key) {
// check if same opportunity exists in the vector
for opportunity_existing in opportunities_existing.clone() {
if opportunity_existing == opportunity {
return Err(RestError::BadParameters(
"Duplicate opportunity submission".to_string(),
));
}
}

opportunities_existing.push(opportunity);
} else {
write_lock.insert(params.permission_key.clone(), vec![opportunity]);
}

tracing::debug!("number of permission keys: {}", write_lock.len());
tracing::debug!(
"number of opportunities for key: {}",
write_lock[&params.permission_key].len()
);

Ok(OpportunityParamsWithId {
opportunity_id: id,
Expand Down Expand Up @@ -144,11 +170,14 @@ pub async fn get_opportunities(
.await
.values()
.cloned()
.map(|opportunity| OpportunityParamsWithId {
opportunity_id: opportunity.id,
params: opportunity.params,
.map(|opportunities_key| {
opportunities_key
.last()
.expect("A permission key vector should have at least one opportunity")
.clone()
.into()
})
.filter(|params_with_id| {
.filter(|params_with_id: &OpportunityParamsWithId| {
let params = match &params_with_id.params {
OpportunityParams::V1(params) => params,
};
Expand Down Expand Up @@ -199,7 +228,7 @@ pub async fn post_bid(
Path(opportunity_id): Path<Uuid>,
Json(opportunity_bid): Json<OpportunityBid>,
) -> Result<Json<BidResult>, RestError> {
let opportunity = store
let opportunities = store
.liquidation_store
.opportunities
.read()
Expand All @@ -208,12 +237,10 @@ pub async fn post_bid(
.ok_or(RestError::OpportunityNotFound)?
.clone();


if opportunity.id != opportunity_id {
return Err(RestError::BadParameters(
"Invalid opportunity_id".to_string(),
));
}
let opportunity = opportunities
.iter()
.find(|o| o.id == opportunity_id)
.ok_or(RestError::OpportunityNotFound)?;

// TODO: move this logic to searcher side
if opportunity.bidders.contains(&opportunity_bid.liquidator) {
Expand Down Expand Up @@ -253,9 +280,13 @@ pub async fn post_bid(
{
Ok(_) => {
let mut write_guard = store.liquidation_store.opportunities.write().await;
let liquidation = write_guard.get_mut(&opportunity_bid.permission_key);
if let Some(liquidation) = liquidation {
liquidation.bidders.insert(opportunity_bid.liquidator);
let opportunities = write_guard.get_mut(&opportunity_bid.permission_key);
if let Some(opportunities) = opportunities {
let opportunity = opportunities
.iter_mut()
.find(|o| o.id == opportunity_id)
.ok_or(RestError::OpportunityNotFound)?;
opportunity.bidders.insert(opportunity_bid.liquidator);
}
Ok(BidResult {
status: "OK".to_string(),
Expand Down
41 changes: 29 additions & 12 deletions auction-server/src/liquidation_adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,25 +333,42 @@ async fn verify_with_store(opportunity: LiquidationOpportunity, store: &Store) -
/// # Arguments
///
/// * `store`: server store
pub async fn run_verification_loop(store: Arc<Store>) {
pub async fn run_verification_loop(store: Arc<Store>) -> Result<()> {
tracing::info!("Starting opportunity verifier...");
while !SHOULD_EXIT.load(Ordering::Acquire) {
let all_opportunities = store.liquidation_store.opportunities.read().await.clone();
for (permission_key, opportunity) in all_opportunities.iter() {
match verify_with_store(opportunity.clone(), &store).await {
Ok(_) => {}
Err(e) => {
store
.liquidation_store
.opportunities
.write()
.await
.remove(permission_key);
tracing::info!("Removed Opportunity with failed verification: {}", e);
for (permission_key, opportunities) in all_opportunities.iter() {
// check each of the opportunities for this permission key for validity
let mut opps_to_remove = vec![];
for opportunity in opportunities.iter() {
match verify_with_store(opportunity.clone(), &store).await {
Ok(_) => {}
Err(e) => {
opps_to_remove.push(opportunity.id);
tracing::info!(
"Removing Opportunity {} with failed verification: {}",
opportunity.id,
e
);
}
}
}

// set write lock to remove all these opportunities
let mut write_lock = store.liquidation_store.opportunities.write().await;

if let Some(opportunities) = write_lock.get_mut(permission_key) {
opportunities.retain(|x| !opps_to_remove.contains(&x.id));
if opportunities.is_empty() {
write_lock.remove(permission_key);
}
}

// release the write lock
drop(write_lock);
}
tokio::time::sleep(Duration::from_secs(5)).await; // this should be replaced by a subscription to the chain and trigger on new blocks
}
tracing::info!("Shutting down opportunity verifier...");
Ok(())
}
10 changes: 5 additions & 5 deletions auction-server/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub struct SimulatedBid {

pub type UnixTimestamp = i64;

#[derive(Serialize, Deserialize, ToSchema, Clone)]
#[derive(Serialize, Deserialize, ToSchema, Clone, PartialEq)]
pub struct TokenQty {
/// Token contract address
#[schema(example = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",value_type=String)]
Expand All @@ -55,7 +55,7 @@ pub struct TokenQty {
/// If a searcher signs the opportunity and have approved enough tokens to liquidation adapter,
/// by calling this contract with the given calldata and structures, they will receive the tokens specified
/// in the receipt_tokens field, and will send the tokens specified in the repay_tokens field.
#[derive(Serialize, Deserialize, ToSchema, Clone)]
#[derive(Serialize, Deserialize, ToSchema, Clone, PartialEq)]
pub struct OpportunityParamsV1 {
/// The permission key required for succesful execution of the liquidation.
#[schema(example = "0xdeadbeefcafe", value_type=String)]
Expand All @@ -78,14 +78,14 @@ pub struct OpportunityParamsV1 {
pub receipt_tokens: Vec<TokenQty>,
}

#[derive(Serialize, Deserialize, ToSchema, Clone)]
#[derive(Serialize, Deserialize, ToSchema, Clone, PartialEq)]
#[serde(tag = "version")]
pub enum OpportunityParams {
#[serde(rename = "v1")]
V1(OpportunityParamsV1),
}

#[derive(Clone)]
#[derive(Clone, PartialEq)]
pub struct LiquidationOpportunity {
pub id: Uuid,
pub creation_time: UnixTimestamp,
Expand All @@ -112,7 +112,7 @@ pub struct ChainStore {

#[derive(Default)]
pub struct LiquidationStore {
pub opportunities: RwLock<HashMap<PermissionKey, LiquidationOpportunity>>,
pub opportunities: RwLock<HashMap<PermissionKey, Vec<LiquidationOpportunity>>>,
}

pub struct Store {
Expand Down
Loading

0 comments on commit 6da5e4c

Please sign in to comment.