Skip to content

Commit

Permalink
Move fee policy database structs (#2657)
Browse files Browse the repository at this point in the history
# Description
Moves `struct FeePolicy`, used to read/write fee policies from database,
to database crate, for reuse. A prerequisite for
#2642 (comment),
as we need to expose fee policies as a new field in `trades` query, and
for this we need to reuse `FeePolicy` struct.

The code change is mostly just a move, no new functionality.

## How to test
Existing tests.
  • Loading branch information
sunce86 authored Apr 25, 2024
1 parent 3927bf4 commit 3c5001e
Show file tree
Hide file tree
Showing 5 changed files with 196 additions and 224 deletions.
160 changes: 6 additions & 154 deletions crates/autopilot/src/database/fee_policies.rs
Original file line number Diff line number Diff line change
@@ -1,166 +1,18 @@
use {
crate::{domain, infra::persistence::dto},
sqlx::{PgConnection, QueryBuilder},
sqlx::PgConnection,
};

pub async fn insert_batch(
ex: &mut PgConnection,
auction_id: domain::auction::Id,
fee_policies: impl IntoIterator<Item = (domain::OrderUid, Vec<domain::fee::Policy>)>,
) -> Result<(), sqlx::Error> {
let mut fee_policies = fee_policies
.into_iter()
.flat_map(|(order_uid, policies)| {
policies
.into_iter()
.map(move |policy| dto::FeePolicy::from_domain(auction_id, order_uid, policy))
})
.peekable();

if fee_policies.peek().is_none() {
return Ok(());
}

let mut query_builder = QueryBuilder::new(
"INSERT INTO fee_policies (auction_id, order_uid, kind, surplus_factor, \
surplus_max_volume_factor, volume_factor, price_improvement_factor, \
price_improvement_max_volume_factor)",
);

query_builder.push_values(fee_policies, |mut b, fee_policy| {
b.push_bind(fee_policy.auction_id)
.push_bind(fee_policy.order_uid)
.push_bind(fee_policy.kind)
.push_bind(fee_policy.surplus_factor)
.push_bind(fee_policy.surplus_max_volume_factor)
.push_bind(fee_policy.volume_factor)
.push_bind(fee_policy.price_improvement_factor)
.push_bind(fee_policy.price_improvement_max_volume_factor);
});

query_builder.build().execute(ex).await.map(|_| ())
}

#[cfg(test)]
mod tests {
use {
super::*,
crate::domain::fee::FeeFactor,
database::byte_array::ByteArray,
sqlx::Connection,
};

pub async fn fetch(
ex: &mut PgConnection,
auction_id: dto::AuctionId,
order_uid: database::OrderUid,
) -> Result<Vec<dto::FeePolicy>, sqlx::Error> {
const QUERY: &str = r#"
SELECT * FROM fee_policies
WHERE auction_id = $1 AND order_uid = $2
ORDER BY application_order
"#;
let rows = sqlx::query_as::<_, dto::FeePolicy>(QUERY)
.bind(auction_id)
.bind(order_uid)
.fetch_all(ex)
.await?
let fee_policies = fee_policies.into_iter().flat_map(|(order_uid, policies)| {
policies
.into_iter()
.collect();
Ok(rows)
}

#[tokio::test]
#[ignore]
async fn postgres_roundtrip() {
let mut db = PgConnection::connect("postgresql://").await.unwrap();
let mut db = db.begin().await.unwrap();
database::clear_DANGER_(&mut db).await.unwrap();

// same primary key for all fee policies
let (auction_id, order_uid) = (1, ByteArray([1; 56]));

// surplus fee policy without caps
let fee_policy_1 = domain::fee::Policy::Surplus {
factor: FeeFactor::try_from(0.1).unwrap(),
max_volume_factor: FeeFactor::try_from(0.99999).unwrap(),
};
// surplus fee policy with caps
let fee_policy_2 = domain::fee::Policy::Surplus {
factor: FeeFactor::try_from(0.2).unwrap(),
max_volume_factor: FeeFactor::try_from(0.05).unwrap(),
};
// volume based fee policy
let fee_policy_3 = domain::fee::Policy::Volume {
factor: FeeFactor::try_from(0.06).unwrap(),
};
// price improvement fee policy
let fee_policy_4 = domain::fee::Policy::PriceImprovement {
factor: FeeFactor::try_from(0.1).unwrap(),
max_volume_factor: FeeFactor::try_from(0.99999).unwrap(),
quote: domain::fee::Quote {
sell_amount: 10.into(),
buy_amount: 20.into(),
fee: 1.into(),
},
};
let input_policies = vec![fee_policy_1, fee_policy_2, fee_policy_3, fee_policy_4];

insert_batch(
&mut db,
auction_id,
vec![(domain::OrderUid(order_uid.0), input_policies)],
)
.await
.unwrap();

// surplus fee policy without caps
let fee_policy_1 = dto::FeePolicy {
auction_id,
order_uid,
kind: dto::fee_policy::FeePolicyKind::Surplus,
surplus_factor: Some(0.1),
surplus_max_volume_factor: Some(0.99999),
volume_factor: None,
price_improvement_factor: None,
price_improvement_max_volume_factor: None,
};
// surplus fee policy with caps
let fee_policy_2 = dto::FeePolicy {
auction_id,
order_uid,
kind: dto::fee_policy::FeePolicyKind::Surplus,
surplus_factor: Some(0.2),
surplus_max_volume_factor: Some(0.05),
volume_factor: None,
price_improvement_factor: None,
price_improvement_max_volume_factor: None,
};
// volume based fee policy
let fee_policy_3 = dto::FeePolicy {
auction_id,
order_uid,
kind: dto::fee_policy::FeePolicyKind::Volume,
surplus_factor: None,
surplus_max_volume_factor: None,
volume_factor: Some(0.06),
price_improvement_factor: None,
price_improvement_max_volume_factor: None,
};
// price improvement fee policy
let fee_policy_4 = dto::FeePolicy {
auction_id,
order_uid,
kind: dto::fee_policy::FeePolicyKind::PriceImprovement,
surplus_factor: None,
surplus_max_volume_factor: None,
volume_factor: None,
price_improvement_factor: Some(0.1),
price_improvement_max_volume_factor: Some(0.99999),
};
let expected = vec![fee_policy_1, fee_policy_2, fee_policy_3, fee_policy_4];
.map(move |policy| dto::fee_policy::from_domain(auction_id, order_uid, policy))
});

let output = fetch(&mut db, 1, order_uid).await.unwrap();
assert_eq!(output, expected);
}
database::fee_policies::insert_batch(ex, fee_policies).await
}
113 changes: 47 additions & 66 deletions crates/autopilot/src/infra/persistence/dto/fee_policy.rs
Original file line number Diff line number Diff line change
@@ -1,69 +1,50 @@
use crate::{boundary, domain};
use {
crate::{boundary, domain},
database::fee_policies::{FeePolicy, FeePolicyKind},
};

#[derive(Debug, Clone, PartialEq, sqlx::FromRow)]
pub struct FeePolicy {
pub auction_id: database::auction::AuctionId,
pub order_uid: boundary::database::OrderUid,
pub kind: FeePolicyKind,
pub surplus_factor: Option<f64>,
pub surplus_max_volume_factor: Option<f64>,
pub volume_factor: Option<f64>,
pub price_improvement_factor: Option<f64>,
pub price_improvement_max_volume_factor: Option<f64>,
}

impl FeePolicy {
pub fn from_domain(
auction_id: domain::auction::Id,
order_uid: domain::OrderUid,
policy: domain::fee::Policy,
) -> Self {
match policy {
domain::fee::Policy::Surplus {
factor,
max_volume_factor,
} => Self {
auction_id,
order_uid: boundary::database::byte_array::ByteArray(order_uid.0),
kind: FeePolicyKind::Surplus,
surplus_factor: Some(factor.into()),
surplus_max_volume_factor: Some(max_volume_factor.into()),
volume_factor: None,
price_improvement_factor: None,
price_improvement_max_volume_factor: None,
},
domain::fee::Policy::Volume { factor } => Self {
auction_id,
order_uid: boundary::database::byte_array::ByteArray(order_uid.0),
kind: FeePolicyKind::Volume,
surplus_factor: None,
surplus_max_volume_factor: None,
volume_factor: Some(factor.into()),
price_improvement_factor: None,
price_improvement_max_volume_factor: None,
},
domain::fee::Policy::PriceImprovement {
factor,
max_volume_factor,
quote: _,
} => Self {
auction_id,
order_uid: boundary::database::byte_array::ByteArray(order_uid.0),
kind: FeePolicyKind::PriceImprovement,
surplus_factor: None,
surplus_max_volume_factor: None,
volume_factor: None,
price_improvement_factor: Some(factor.into()),
price_improvement_max_volume_factor: Some(max_volume_factor.into()),
},
}
pub fn from_domain(
auction_id: domain::auction::Id,
order_uid: domain::OrderUid,
policy: domain::fee::Policy,
) -> FeePolicy {
match policy {
domain::fee::Policy::Surplus {
factor,
max_volume_factor,
} => FeePolicy {
auction_id,
order_uid: boundary::database::byte_array::ByteArray(order_uid.0),
kind: FeePolicyKind::Surplus,
surplus_factor: Some(factor.into()),
surplus_max_volume_factor: Some(max_volume_factor.into()),
volume_factor: None,
price_improvement_factor: None,
price_improvement_max_volume_factor: None,
},
domain::fee::Policy::Volume { factor } => FeePolicy {
auction_id,
order_uid: boundary::database::byte_array::ByteArray(order_uid.0),
kind: FeePolicyKind::Volume,
surplus_factor: None,
surplus_max_volume_factor: None,
volume_factor: Some(factor.into()),
price_improvement_factor: None,
price_improvement_max_volume_factor: None,
},
domain::fee::Policy::PriceImprovement {
factor,
max_volume_factor,
quote: _,
} => FeePolicy {
auction_id,
order_uid: boundary::database::byte_array::ByteArray(order_uid.0),
kind: FeePolicyKind::PriceImprovement,
surplus_factor: None,
surplus_max_volume_factor: None,
volume_factor: None,
price_improvement_factor: Some(factor.into()),
price_improvement_max_volume_factor: Some(max_volume_factor.into()),
},
}
}

#[derive(Debug, Clone, PartialEq, sqlx::Type)]
#[sqlx(type_name = "PolicyKind", rename_all = "lowercase")]
pub enum FeePolicyKind {
Surplus,
Volume,
PriceImprovement,
}
5 changes: 1 addition & 4 deletions crates/autopilot/src/infra/persistence/dto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,4 @@ pub mod fee_policy;
pub mod order;
pub mod quote;

pub use {
auction::{Auction, AuctionId, AuctionWithId},
fee_policy::FeePolicy,
};
pub use auction::{Auction, AuctionId, AuctionWithId};
Loading

0 comments on commit 3c5001e

Please sign in to comment.