Skip to content

Commit

Permalink
feat: custom expiration for submarine swaps (#779)
Browse files Browse the repository at this point in the history
  • Loading branch information
michael1011 authored Jan 14, 2025
1 parent a47bd78 commit 5d16749
Show file tree
Hide file tree
Showing 20 changed files with 649 additions and 167 deletions.
1 change: 1 addition & 0 deletions boltzr/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion boltzr/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ axum-prometheus = { version = "0.6.1", default-features = false, optional = true
metrics = { version = "0.24.1", optional = true }
diesel_migrations = "2.2.0"
r2d2 = "0.8.10"
diesel = { version = "2.2.6", default-features = false, features = ["postgres", "r2d2", "chrono"] }
diesel = { version = "2.2.6", default-features = false, features = ["postgres", "r2d2", "chrono", "serde_json"] }
strum_macros = "0.26.4"
strum = "0.26.3"
dashmap = "6.1.0"
Expand Down
2 changes: 1 addition & 1 deletion boltzr/src/api/ws/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub struct ChannelInfo {
pub funding_transaction_vout: u64,
}

#[derive(Deserialize, Serialize, Debug, Clone, PartialEq)]
#[derive(Deserialize, Serialize, Default, Debug, Clone, PartialEq)]
pub struct SwapStatus {
pub id: String,
pub status: String,
Expand Down
1 change: 1 addition & 0 deletions boltzr/src/db/helpers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use diesel::sql_types::Bool;
use diesel::BoxableExpression;

pub mod chain_swap;
pub mod referral;
pub mod reverse_swap;
pub mod swap;
pub mod web_hook;
Expand Down
31 changes: 31 additions & 0 deletions boltzr/src/db/helpers/referral.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use crate::db::helpers::{BoxedCondition, QueryResponse};
use crate::db::models::Referral;
use crate::db::schema::referrals;
use crate::db::Pool;
use diesel::{QueryDsl, RunQueryDsl, SelectableHelper};

pub type ReferralCondition = BoxedCondition<referrals::table>;

pub trait ReferralHelper {
fn get_all(&self, condition: ReferralCondition) -> QueryResponse<Vec<Referral>>;
}

#[derive(Clone, Debug)]
pub struct ReferralHelperDatabase {
pool: Pool,
}

impl ReferralHelperDatabase {
pub fn new(pool: Pool) -> Self {
Self { pool }
}
}

impl ReferralHelper for ReferralHelperDatabase {
fn get_all(&self, condition: ReferralCondition) -> QueryResponse<Vec<Referral>> {
Ok(referrals::dsl::referrals
.select(Referral::as_select())
.filter(condition)
.load(&mut self.pool.get()?)?)
}
}
7 changes: 5 additions & 2 deletions boltzr/src/db/models/chain_swap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ use crate::swap::SwapUpdate;
use crate::utils::pair::{split_pair, OrderSide};
use diesel::{AsChangeset, Associations, Identifiable, Insertable, Queryable, Selectable};

#[derive(Queryable, Selectable, Insertable, Identifiable, AsChangeset, PartialEq, Clone, Debug)]
#[derive(
Queryable, Selectable, Insertable, Identifiable, AsChangeset, PartialEq, Default, Clone, Debug,
)]
#[diesel(table_name = crate::db::schema::chainSwaps)]
#[allow(non_snake_case)]
pub struct ChainSwap {
Expand All @@ -21,6 +23,7 @@ pub struct ChainSwap {
AsChangeset,
Associations,
PartialEq,
Default,
Clone,
Debug,
)]
Expand All @@ -36,7 +39,7 @@ pub struct ChainSwapData {
pub transactionVout: Option<i32>,
}

#[derive(Clone, Debug)]
#[derive(Default, Clone, Debug)]
pub struct ChainSwapInfo {
swap: ChainSwap,
sending_data: ChainSwapData,
Expand Down
25 changes: 25 additions & 0 deletions boltzr/src/db/models/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ use crate::swap::SwapUpdate;
use strum_macros::{Display, EnumString};

mod chain_swap;
mod referral;
mod reverse_swap;
mod swap;
mod web_hook;

pub use chain_swap::*;
pub use referral::*;
pub use reverse_swap::*;
pub use swap::*;
pub use web_hook::*;
Expand All @@ -21,6 +23,29 @@ pub enum SwapType {
Chain,
}

impl TryFrom<u64> for SwapType {
type Error = anyhow::Error;

fn try_from(value: u64) -> Result<Self, Self::Error> {
match value {
0 => Ok(SwapType::Submarine),
1 => Ok(SwapType::Reverse),
2 => Ok(SwapType::Chain),
_ => Err(anyhow::anyhow!("invalid value for SwapType: {}", value)),
}
}
}

impl From<SwapType> for u64 {
fn from(value: SwapType) -> Self {
match value {
SwapType::Submarine => 0,
SwapType::Reverse => 1,
SwapType::Chain => 2,
}
}
}

pub trait SomeSwap {
fn kind(&self) -> SwapType;

Expand Down
158 changes: 158 additions & 0 deletions boltzr/src/db/models/referral.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
use crate::db::models::SwapType;
use anyhow::anyhow;
use diesel::{AsChangeset, Insertable, Queryable, Selectable};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

type CustomExpirations = HashMap<String, u64>;

#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct PairConfig {
expirations: Option<CustomExpirations>,
}

impl PairConfig {
fn get_expiration(&self, kind: SwapType) -> Option<u64> {
self.expirations.as_ref().map(|expirations| {
expirations
.get(&<SwapType as Into<u64>>::into(kind).to_string())
.copied()
})?
}
}

#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Config {
#[serde(flatten)]
pub base: PairConfig,

pairs: Option<HashMap<String, PairConfig>>,
}

impl Config {
fn for_pair(&self, pair: &str) -> &PairConfig {
self.pairs
.as_ref()
.map(|pairs| pairs.get(pair).unwrap_or(&self.base))
.unwrap_or(&self.base)
}
}

#[derive(Queryable, Selectable, Insertable, AsChangeset, PartialEq, Clone, Debug)]
#[diesel(table_name = crate::db::schema::referrals)]
pub struct Referral {
pub id: String,
pub config: Option<serde_json::Value>,
}

impl Referral {
pub fn custom_expiration_secs(
&self,
pair: &str,
kind: SwapType,
) -> anyhow::Result<Option<u64>> {
if let Some(cfg) = self.parse_config()? {
return Ok(match cfg.for_pair(pair).get_expiration(kind) {
Some(expiration) => Some(expiration),
None => cfg.base.get_expiration(kind),
});
}

Ok(None)
}

fn parse_config(&self) -> anyhow::Result<Option<Config>> {
if let Some(cfg) = &self.config {
return match serde_json::from_value(cfg.clone()) {
Ok(config) => Ok(Some(config)),
Err(err) => Err(anyhow!(
"could not parse config of referral {}: {}",
self.id,
err
)),
};
}

Ok(None)
}
}

#[cfg(test)]
mod tests {
use super::*;
use rstest::*;

#[rstest]
#[case("{\"expirations\": {\"0\": 600}}", SwapType::Submarine, Some(600))]
#[case(
"{\"expirations\": {\"0\": 600, \"1\": 123}}",
SwapType::Reverse,
Some(123)
)]
#[case(
"{\"expirations\": {\"0\": 600, \"1\": 123}}",
SwapType::Submarine,
Some(600)
)]
#[case("{\"expirations\": {\"0\": 600, \"1\": 123}}", SwapType::Chain, None)]
fn test_pair_config_get_expiration(
#[case] json: &str,
#[case] kind: SwapType,
#[case] expected_expiration: Option<u64>,
) {
let config = serde_json::from_str::<PairConfig>(json).unwrap();
assert_eq!(config.get_expiration(kind), expected_expiration);
}

#[rstest]
#[case("", "{\"expirations\": {\"0\": 123}}")]
#[case("L-BTC/BTC", "{\"expirations\": {\"0\": 123}}")]
#[case("BTC/BTC", "{\"expirations\": {\"1\": 2121}}")]
fn test_config_for_pair(#[case] pair: &str, #[case] expected: &str) {
let config = serde_json::from_str::<Config>(
"{\"expirations\": {\"0\": 123}, \"pairs\": {\"BTC/BTC\": {\"expirations\": {\"1\": 2121}}}}",
)
.unwrap();

let expected = serde_json::from_str::<PairConfig>(expected).unwrap();
assert_eq!(config.for_pair(pair), &expected);
}

#[rstest]
#[case("BTC/BTC", SwapType::Submarine, Some(600))]
#[case("L-BTC/BTC", SwapType::Submarine, Some(123))]
#[case("BTC/BTC", SwapType::Reverse, Some(2121))]
#[case("L-BTC/BTC", SwapType::Reverse, Some(2121))]
#[case("BTC/BTC", SwapType::Chain, None)]
fn test_referral_custom_expiration(
#[case] pair: &str,
#[case] kind: SwapType,
#[case] expected_expiration: Option<u64>,
) {
let config = serde_json::json!({
"expirations": {
"0": 123,
"1": 2121
},
"pairs": {
"BTC/BTC": {
"expirations": {
"0": 600
}
}
}
});
let referral = Referral {
id: "id".to_string(),
config: Some(config),
};

let res = referral.custom_expiration_secs(pair, kind).unwrap();

if let Some(expected_expiration) = expected_expiration {
assert_eq!(res.unwrap(), expected_expiration);
} else {
assert!(res.is_none());
}
}
}
2 changes: 1 addition & 1 deletion boltzr/src/db/models/reverse_swap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::swap::SwapUpdate;
use crate::utils::pair::{split_pair, OrderSide};
use diesel::{AsChangeset, Insertable, Queryable, Selectable};

#[derive(Queryable, Selectable, Insertable, AsChangeset, PartialEq, Clone, Debug)]
#[derive(Queryable, Selectable, Insertable, AsChangeset, PartialEq, Default, Clone, Debug)]
#[diesel(table_name = crate::db::schema::reverseSwaps)]
#[allow(non_snake_case)]
pub struct ReverseSwap {
Expand Down
8 changes: 4 additions & 4 deletions boltzr/src/db/models/swap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,19 @@ use crate::swap::SwapUpdate;
use crate::utils::pair::{split_pair, OrderSide};
use diesel::{AsChangeset, Insertable, Queryable, Selectable};

#[derive(Queryable, Selectable, Insertable, AsChangeset, PartialEq, Clone, Debug)]
#[derive(Queryable, Selectable, Insertable, AsChangeset, PartialEq, Default, Clone, Debug)]
#[diesel(table_name = crate::db::schema::swaps)]
#[allow(non_snake_case)]
pub struct Swap {
pub id: String,
pub referral: Option<String>,
pub pair: String,
pub orderSide: i32,
pub status: String,
pub failureReason: Option<String>,
pub invoice: Option<String>,
pub lockupAddress: String,
pub createdAt: chrono::NaiveDateTime,
}

impl SomeSwap for Swap {
Expand Down Expand Up @@ -94,11 +96,9 @@ mod test {
Swap {
id: "swap id".to_string(),
pair: "L-BTC/BTC".to_string(),
lockupAddress: "".to_string(),
status: "transaction.mempool".to_string(),
orderSide: order_side.unwrap_or(OrderSide::Buy) as i32,
invoice: None,
failureReason: None,
..Default::default()
}
}
}
9 changes: 9 additions & 0 deletions boltzr/src/db/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,25 @@ diesel::table! {
}
}

diesel::table! {
referrals (id) {
id -> Text,
config -> Nullable<Json>,
}
}

diesel::table! {
#[allow(non_snake_case)]
swaps (id) {
id -> Text,
referral -> Nullable<Text>,
pair -> Text,
orderSide -> Integer,
status -> Text,
failureReason -> Nullable<Text>,
invoice -> Nullable<Text>,
lockupAddress -> Text,
createdAt -> Timestamp,
}
}

Expand Down
Loading

0 comments on commit 5d16749

Please sign in to comment.