Browse Source

Merge #877

877: Remove oracle event id from cfd r=da-kami a=da-kami

Loading the CFD by oracle event id, based on the "initial oracle event id" that we store in the CFD is wrong.
We should actually not store this even id at all, as it only becomes relevant once the setup is completed and we have a DLC.
If we load based on the event id that was stored in the CFD we might still load based on an outdated id once we roll over!
Thus, the event id of the cfd was removed. We always use the one stored in the DLC.
Since it is very hard to load a CFD based on the event id in the DLC (because that only exists in some states) this was removed.
Instead, upon attestation, we load all CFDs and then decide if we care about the attestation.
This should be optimized to loading all **open** CFDs at some point.

Co-authored-by: Daniel Karzel <daniel@comit.network>
test-force-close-without-fake-clock
bors[bot] 3 years ago
committed by GitHub
parent
commit
1441c8fd73
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 29
      daemon/migrations/20211213000000_remove-oracle_event_id_from_cfd.sql
  2. 132
      daemon/sqlx-data.json
  3. 7
      daemon/src/cfd_actors.rs
  4. 178
      daemon/src/db.rs
  5. 2
      daemon/src/maker_cfd.rs
  6. 6
      daemon/src/model/cfd.rs
  7. 8
      daemon/src/projection.rs
  8. 4
      daemon/src/taker_cfd.rs
  9. 2
      maker-frontend/src/components/Types.tsx
  10. 4
      maker-frontend/src/components/cfdtables/CfdTable.tsx
  11. 3
      taker-frontend/src/components/CloseButton.tsx
  12. 2
      taker-frontend/src/types.ts

29
daemon/migrations/20211213000000_remove-oracle_event_id_from_cfd.sql

@ -0,0 +1,29 @@
drop table cfd_states;
drop table cfds;
create table if not exists cfds
(
id integer primary key autoincrement,
uuid text unique not null,
trading_pair text not null,
position text not null,
initial_price text not null,
leverage integer not null,
liquidation_price text not null,
creation_timestamp_seconds integer not null,
settlement_time_interval_seconds integer not null,
origin text not null,
fee_rate integer not null,
quantity_usd text not null,
counterparty text not null
);
create unique index if not exists cfd_uuid on cfds (uuid);
create table if not exists cfd_states
(
id integer primary key autoincrement,
cfd_id integer not null,
state text not null,
foreign key (cfd_id) references cfds (id)
);

132
daemon/sqlx-data.json

@ -18,8 +18,8 @@
] ]
} }
}, },
"3efef7791c6a33eff33426e4b68e1f908be8da5efd2f991ba8e67df99f7ab360": { "368233f70eed9dff137d884994a4863063459d53be75ed97bbf5297df70ffdb1": {
"query": "\n with state as (\n select\n cfd_id,\n state\n from cfd_states\n inner join cfds on cfds.id = cfd_states.cfd_id\n where cfd_states.id in (\n select\n max(id) as id\n from cfd_states\n group by (cfd_id)\n )\n )\n\n select\n cfds.uuid as \"uuid: crate::model::cfd::OrderId\",\n cfds.trading_pair as \"trading_pair: crate::model::TradingPair\",\n cfds.position as \"position: crate::model::Position\",\n cfds.initial_price as \"initial_price: crate::model::Price\",\n cfds.leverage as \"leverage: crate::model::Leverage\",\n cfds.liquidation_price as \"liquidation_price: crate::model::Price\",\n cfds.creation_timestamp_seconds as \"creation_timestamp_seconds: crate::model::Timestamp\",\n cfds.settlement_time_interval_seconds as \"settlement_time_interval_secs: i64\",\n cfds.origin as \"origin: crate::model::cfd::Origin\",\n cfds.oracle_event_id as \"oracle_event_id: crate::model::BitMexPriceEventId\",\n cfds.fee_rate as \"fee_rate: u32\",\n cfds.quantity_usd as \"quantity_usd: crate::model::Usd\",\n cfds.counterparty as \"counterparty: crate::model::Identity\",\n state.state\n\n from cfds\n inner join state on state.cfd_id = cfds.id\n\n where cfds.oracle_event_id = $1\n ", "query": "\n with state as (\n select\n cfd_id,\n state\n from cfd_states\n inner join cfds on cfds.id = cfd_states.cfd_id\n where cfd_states.id in (\n select\n max(id) as id\n from cfd_states\n group by (cfd_id)\n )\n )\n\n select\n cfds.uuid as \"uuid: crate::model::cfd::OrderId\",\n cfds.trading_pair as \"trading_pair: crate::model::TradingPair\",\n cfds.position as \"position: crate::model::Position\",\n cfds.initial_price as \"initial_price: crate::model::Price\",\n cfds.leverage as \"leverage: crate::model::Leverage\",\n cfds.liquidation_price as \"liquidation_price: crate::model::Price\",\n cfds.creation_timestamp_seconds as \"creation_timestamp_seconds: crate::model::Timestamp\",\n cfds.settlement_time_interval_seconds as \"settlement_time_interval_secs: i64\",\n cfds.origin as \"origin: crate::model::cfd::Origin\",\n cfds.fee_rate as \"fee_rate: u32\",\n cfds.quantity_usd as \"quantity_usd: crate::model::Usd\",\n cfds.counterparty as \"counterparty: crate::model::Identity\",\n state.state\n\n from cfds\n inner join state on state.cfd_id = cfds.id\n\n where cfds.uuid = $1\n ",
"describe": { "describe": {
"columns": [ "columns": [
{ {
@ -67,29 +67,24 @@
"ordinal": 8, "ordinal": 8,
"type_info": "Text" "type_info": "Text"
}, },
{
"name": "oracle_event_id: crate::model::BitMexPriceEventId",
"ordinal": 9,
"type_info": "Text"
},
{ {
"name": "fee_rate: u32", "name": "fee_rate: u32",
"ordinal": 10, "ordinal": 9,
"type_info": "Int64" "type_info": "Int64"
}, },
{ {
"name": "quantity_usd: crate::model::Usd", "name": "quantity_usd: crate::model::Usd",
"ordinal": 11, "ordinal": 10,
"type_info": "Text" "type_info": "Text"
}, },
{ {
"name": "counterparty: crate::model::Identity", "name": "counterparty: crate::model::Identity",
"ordinal": 12, "ordinal": 11,
"type_info": "Text" "type_info": "Text"
}, },
{ {
"name": "state", "name": "state",
"ordinal": 13, "ordinal": 12,
"type_info": "Text" "type_info": "Text"
} }
], ],
@ -109,7 +104,6 @@
false, false,
false, false,
false, false,
false,
false false
] ]
} }
@ -132,8 +126,8 @@
] ]
} }
}, },
"c64374031a424b78b1061d05d0087d79c7251fe6da1dd3cb5d146d1e2b4dd12f": { "e38578559a3a9bd5a082579cecc52c976f55f2ed25e3bedebab85bb8dcd69354": {
"query": "\n with state as (\n select\n cfd_id,\n state\n from cfd_states\n inner join cfds on cfds.id = cfd_states.cfd_id\n where cfd_states.id in (\n select\n max(id) as id\n from cfd_states\n group by (cfd_id)\n )\n )\n\n select\n cfds.uuid as \"uuid: crate::model::cfd::OrderId\",\n cfds.trading_pair as \"trading_pair: crate::model::TradingPair\",\n cfds.position as \"position: crate::model::Position\",\n cfds.initial_price as \"initial_price: crate::model::Price\",\n cfds.leverage as \"leverage: crate::model::Leverage\",\n cfds.liquidation_price as \"liquidation_price: crate::model::Price\",\n cfds.creation_timestamp_seconds as \"creation_timestamp_seconds: crate::model::Timestamp\",\n cfds.settlement_time_interval_seconds as \"settlement_time_interval_secs: i64\",\n cfds.origin as \"origin: crate::model::cfd::Origin\",\n cfds.oracle_event_id as \"oracle_event_id: crate::model::BitMexPriceEventId\",\n cfds.fee_rate as \"fee_rate: u32\",\n cfds.quantity_usd as \"quantity_usd: crate::model::Usd\",\n cfds.counterparty as \"counterparty: crate::model::Identity\",\n state.state\n\n from cfds\n inner join state on state.cfd_id = cfds.id\n ", "query": "\n with state as (\n select\n cfd_id,\n state\n from cfd_states\n inner join cfds on cfds.id = cfd_states.cfd_id\n where cfd_states.id in (\n select\n max(id) as id\n from cfd_states\n group by (cfd_id)\n )\n )\n\n select\n cfds.uuid as \"uuid: crate::model::cfd::OrderId\",\n cfds.trading_pair as \"trading_pair: crate::model::TradingPair\",\n cfds.position as \"position: crate::model::Position\",\n cfds.initial_price as \"initial_price: crate::model::Price\",\n cfds.leverage as \"leverage: crate::model::Leverage\",\n cfds.liquidation_price as \"liquidation_price: crate::model::Price\",\n cfds.creation_timestamp_seconds as \"creation_timestamp_seconds: crate::model::Timestamp\",\n cfds.settlement_time_interval_seconds as \"settlement_time_interval_secs: i64\",\n cfds.origin as \"origin: crate::model::cfd::Origin\",\n cfds.fee_rate as \"fee_rate: u32\",\n cfds.quantity_usd as \"quantity_usd: crate::model::Usd\",\n cfds.counterparty as \"counterparty: crate::model::Identity\",\n state.state\n\n from cfds\n inner join state on state.cfd_id = cfds.id\n ",
"describe": { "describe": {
"columns": [ "columns": [
{ {
@ -181,130 +175,29 @@
"ordinal": 8, "ordinal": 8,
"type_info": "Text" "type_info": "Text"
}, },
{
"name": "oracle_event_id: crate::model::BitMexPriceEventId",
"ordinal": 9,
"type_info": "Text"
},
{ {
"name": "fee_rate: u32", "name": "fee_rate: u32",
"ordinal": 10,
"type_info": "Int64"
},
{
"name": "quantity_usd: crate::model::Usd",
"ordinal": 11,
"type_info": "Text"
},
{
"name": "counterparty: crate::model::Identity",
"ordinal": 12,
"type_info": "Text"
},
{
"name": "state",
"ordinal": 13,
"type_info": "Text"
}
],
"parameters": {
"Right": 0
},
"nullable": [
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false
]
}
},
"ff0e5909f36d03a19434acef0bf307c5b8a70beae6f619853f4e3ce5a8c53b61": {
"query": "\n with state as (\n select\n cfd_id,\n state\n from cfd_states\n inner join cfds on cfds.id = cfd_states.cfd_id\n where cfd_states.id in (\n select\n max(id) as id\n from cfd_states\n group by (cfd_id)\n )\n )\n\n select\n cfds.uuid as \"uuid: crate::model::cfd::OrderId\",\n cfds.trading_pair as \"trading_pair: crate::model::TradingPair\",\n cfds.position as \"position: crate::model::Position\",\n cfds.initial_price as \"initial_price: crate::model::Price\",\n cfds.leverage as \"leverage: crate::model::Leverage\",\n cfds.liquidation_price as \"liquidation_price: crate::model::Price\",\n cfds.creation_timestamp_seconds as \"creation_timestamp_seconds: crate::model::Timestamp\",\n cfds.settlement_time_interval_seconds as \"settlement_time_interval_secs: i64\",\n cfds.origin as \"origin: crate::model::cfd::Origin\",\n cfds.oracle_event_id as \"oracle_event_id: crate::model::BitMexPriceEventId\",\n cfds.fee_rate as \"fee_rate: u32\",\n cfds.quantity_usd as \"quantity_usd: crate::model::Usd\",\n cfds.counterparty as \"counterparty: crate::model::Identity\",\n state.state\n\n from cfds\n inner join state on state.cfd_id = cfds.id\n\n where cfds.uuid = $1\n ",
"describe": {
"columns": [
{
"name": "uuid: crate::model::cfd::OrderId",
"ordinal": 0,
"type_info": "Text"
},
{
"name": "trading_pair: crate::model::TradingPair",
"ordinal": 1,
"type_info": "Text"
},
{
"name": "position: crate::model::Position",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "initial_price: crate::model::Price",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "leverage: crate::model::Leverage",
"ordinal": 4,
"type_info": "Int64"
},
{
"name": "liquidation_price: crate::model::Price",
"ordinal": 5,
"type_info": "Text"
},
{
"name": "creation_timestamp_seconds: crate::model::Timestamp",
"ordinal": 6,
"type_info": "Int64"
},
{
"name": "settlement_time_interval_secs: i64",
"ordinal": 7,
"type_info": "Int64"
},
{
"name": "origin: crate::model::cfd::Origin",
"ordinal": 8,
"type_info": "Text"
},
{
"name": "oracle_event_id: crate::model::BitMexPriceEventId",
"ordinal": 9, "ordinal": 9,
"type_info": "Text"
},
{
"name": "fee_rate: u32",
"ordinal": 10,
"type_info": "Int64" "type_info": "Int64"
}, },
{ {
"name": "quantity_usd: crate::model::Usd", "name": "quantity_usd: crate::model::Usd",
"ordinal": 11, "ordinal": 10,
"type_info": "Text" "type_info": "Text"
}, },
{ {
"name": "counterparty: crate::model::Identity", "name": "counterparty: crate::model::Identity",
"ordinal": 12, "ordinal": 11,
"type_info": "Text" "type_info": "Text"
}, },
{ {
"name": "state", "name": "state",
"ordinal": 13, "ordinal": 12,
"type_info": "Text" "type_info": "Text"
} }
], ],
"parameters": { "parameters": {
"Right": 1 "Right": 0
}, },
"nullable": [ "nullable": [
false, false,
@ -319,7 +212,6 @@
false, false,
false, false,
false, false,
false,
false false
] ]
} }

7
daemon/src/cfd_actors.rs

@ -147,12 +147,17 @@ where
attestation.id attestation.id
); );
let mut cfds = db::load_cfds_by_oracle_event_id(attestation.id, conn).await?; let mut cfds = db::load_all_cfds(conn).await?;
for (cfd, dlc) in cfds for (cfd, dlc) in cfds
.iter_mut() .iter_mut()
.filter_map(|cfd| cfd.dlc().map(|dlc| (cfd, dlc))) .filter_map(|cfd| cfd.dlc().map(|dlc| (cfd, dlc)))
{ {
if dlc.settlement_event_id != attestation.id {
// If this CFD is not interested in this attestation we ignore it
continue;
}
let attestation = try_continue!(Attestation::new( let attestation = try_continue!(Attestation::new(
attestation.id, attestation.id,
attestation.price, attestation.price,

178
daemon/src/db.rs

@ -1,7 +1,6 @@
use crate::model::cfd::Cfd; use crate::model::cfd::Cfd;
use crate::model::cfd::CfdState; use crate::model::cfd::CfdState;
use crate::model::cfd::OrderId; use crate::model::cfd::OrderId;
use crate::model::BitMexPriceEventId;
use anyhow::Context; use anyhow::Context;
use anyhow::Result; use anyhow::Result;
use sqlx::pool::PoolConnection; use sqlx::pool::PoolConnection;
@ -29,11 +28,10 @@ pub async fn insert_cfd(cfd: &Cfd, conn: &mut PoolConnection<Sqlite>) -> anyhow:
creation_timestamp_seconds, creation_timestamp_seconds,
settlement_time_interval_seconds, settlement_time_interval_seconds,
origin, origin,
oracle_event_id,
fee_rate, fee_rate,
quantity_usd, quantity_usd,
counterparty counterparty
) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13); ) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12);
insert into cfd_states ( insert into cfd_states (
cfd_id, cfd_id,
@ -41,7 +39,7 @@ pub async fn insert_cfd(cfd: &Cfd, conn: &mut PoolConnection<Sqlite>) -> anyhow:
) )
select select
id as cfd_id, id as cfd_id,
$14 as state $13 as state
from cfds from cfds
order by id desc limit 1; order by id desc limit 1;
"#, "#,
@ -55,7 +53,6 @@ pub async fn insert_cfd(cfd: &Cfd, conn: &mut PoolConnection<Sqlite>) -> anyhow:
.bind(&cfd.creation_timestamp) .bind(&cfd.creation_timestamp)
.bind(&cfd.settlement_interval.whole_seconds()) .bind(&cfd.settlement_interval.whole_seconds())
.bind(&cfd.origin) .bind(&cfd.origin)
.bind(&cfd.oracle_event_id)
.bind(&cfd.fee_rate) .bind(&cfd.fee_rate)
.bind(&cfd.quantity_usd) .bind(&cfd.quantity_usd)
.bind(&cfd.counterparty) .bind(&cfd.counterparty)
@ -180,7 +177,6 @@ pub async fn load_cfd_by_order_id(
cfds.creation_timestamp_seconds as "creation_timestamp_seconds: crate::model::Timestamp", cfds.creation_timestamp_seconds as "creation_timestamp_seconds: crate::model::Timestamp",
cfds.settlement_time_interval_seconds as "settlement_time_interval_secs: i64", cfds.settlement_time_interval_seconds as "settlement_time_interval_secs: i64",
cfds.origin as "origin: crate::model::cfd::Origin", cfds.origin as "origin: crate::model::cfd::Origin",
cfds.oracle_event_id as "oracle_event_id: crate::model::BitMexPriceEventId",
cfds.fee_rate as "fee_rate: u32", cfds.fee_rate as "fee_rate: u32",
cfds.quantity_usd as "quantity_usd: crate::model::Usd", cfds.quantity_usd as "quantity_usd: crate::model::Usd",
cfds.counterparty as "counterparty: crate::model::Identity", cfds.counterparty as "counterparty: crate::model::Identity",
@ -209,7 +205,6 @@ pub async fn load_cfd_by_order_id(
creation_timestamp: row.creation_timestamp_seconds, creation_timestamp: row.creation_timestamp_seconds,
settlement_interval: Duration::new(row.settlement_time_interval_secs, 0), settlement_interval: Duration::new(row.settlement_time_interval_secs, 0),
origin: row.origin, origin: row.origin,
oracle_event_id: row.oracle_event_id,
fee_rate: row.fee_rate, fee_rate: row.fee_rate,
quantity_usd: row.quantity_usd, quantity_usd: row.quantity_usd,
state: serde_json::from_str(row.state.as_str())?, state: serde_json::from_str(row.state.as_str())?,
@ -245,7 +240,6 @@ pub async fn load_all_cfds(conn: &mut PoolConnection<Sqlite>) -> anyhow::Result<
cfds.creation_timestamp_seconds as "creation_timestamp_seconds: crate::model::Timestamp", cfds.creation_timestamp_seconds as "creation_timestamp_seconds: crate::model::Timestamp",
cfds.settlement_time_interval_seconds as "settlement_time_interval_secs: i64", cfds.settlement_time_interval_seconds as "settlement_time_interval_secs: i64",
cfds.origin as "origin: crate::model::cfd::Origin", cfds.origin as "origin: crate::model::cfd::Origin",
cfds.oracle_event_id as "oracle_event_id: crate::model::BitMexPriceEventId",
cfds.fee_rate as "fee_rate: u32", cfds.fee_rate as "fee_rate: u32",
cfds.quantity_usd as "quantity_usd: crate::model::Usd", cfds.quantity_usd as "quantity_usd: crate::model::Usd",
cfds.counterparty as "counterparty: crate::model::Identity", cfds.counterparty as "counterparty: crate::model::Identity",
@ -271,80 +265,6 @@ pub async fn load_all_cfds(conn: &mut PoolConnection<Sqlite>) -> anyhow::Result<
creation_timestamp: row.creation_timestamp_seconds, creation_timestamp: row.creation_timestamp_seconds,
settlement_interval: Duration::new(row.settlement_time_interval_secs, 0), settlement_interval: Duration::new(row.settlement_time_interval_secs, 0),
origin: row.origin, origin: row.origin,
oracle_event_id: row.oracle_event_id,
fee_rate: row.fee_rate,
quantity_usd: row.quantity_usd,
state: serde_json::from_str(row.state.as_str())?,
counterparty: row.counterparty,
})
})
.collect::<Result<Vec<_>>>()?;
Ok(cfds)
}
/// Loads all CFDs with the latest state as the CFD state
pub async fn load_cfds_by_oracle_event_id(
oracle_event_id: BitMexPriceEventId,
conn: &mut PoolConnection<Sqlite>,
) -> anyhow::Result<Vec<Cfd>> {
let event_id = oracle_event_id.to_string();
let rows = sqlx::query!(
r#"
with state as (
select
cfd_id,
state
from cfd_states
inner join cfds on cfds.id = cfd_states.cfd_id
where cfd_states.id in (
select
max(id) as id
from cfd_states
group by (cfd_id)
)
)
select
cfds.uuid as "uuid: crate::model::cfd::OrderId",
cfds.trading_pair as "trading_pair: crate::model::TradingPair",
cfds.position as "position: crate::model::Position",
cfds.initial_price as "initial_price: crate::model::Price",
cfds.leverage as "leverage: crate::model::Leverage",
cfds.liquidation_price as "liquidation_price: crate::model::Price",
cfds.creation_timestamp_seconds as "creation_timestamp_seconds: crate::model::Timestamp",
cfds.settlement_time_interval_seconds as "settlement_time_interval_secs: i64",
cfds.origin as "origin: crate::model::cfd::Origin",
cfds.oracle_event_id as "oracle_event_id: crate::model::BitMexPriceEventId",
cfds.fee_rate as "fee_rate: u32",
cfds.quantity_usd as "quantity_usd: crate::model::Usd",
cfds.counterparty as "counterparty: crate::model::Identity",
state.state
from cfds
inner join state on state.cfd_id = cfds.id
where cfds.oracle_event_id = $1
"#,
event_id
)
.fetch_all(conn)
.await?;
let cfds = rows
.into_iter()
.map(|row| {
Ok(Cfd {
id: row.uuid,
trading_pair: row.trading_pair,
position: row.position,
price: row.initial_price,
leverage: row.leverage,
liquidation_price: row.liquidation_price,
creation_timestamp: row.creation_timestamp_seconds,
settlement_interval: Duration::new(row.settlement_time_interval_secs, 0),
origin: row.origin,
oracle_event_id: row.oracle_event_id,
fee_rate: row.fee_rate, fee_rate: row.fee_rate,
quantity_usd: row.quantity_usd, quantity_usd: row.quantity_usd,
state: serde_json::from_str(row.state.as_str())?, state: serde_json::from_str(row.state.as_str())?,
@ -363,15 +283,14 @@ mod tests {
use crate::model::cfd::CfdState; use crate::model::cfd::CfdState;
use crate::model::cfd::Order; use crate::model::cfd::Order;
use crate::model::cfd::Origin; use crate::model::cfd::Origin;
use crate::model::BitMexPriceEventId;
use crate::model::Identity; use crate::model::Identity;
use crate::model::Price; use crate::model::Price;
use crate::model::Usd; use crate::model::Usd;
use crate::seed::Seed; use crate::seed::Seed;
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use rand::Rng;
use rust_decimal_macros::dec; use rust_decimal_macros::dec;
use sqlx::SqlitePool; use sqlx::SqlitePool;
use time::macros::datetime;
use time::OffsetDateTime; use time::OffsetDateTime;
#[tokio::test] #[tokio::test]
@ -408,35 +327,6 @@ mod tests {
assert_eq!(cfd2, loaded_2); assert_eq!(cfd2, loaded_2);
} }
#[tokio::test]
async fn test_insert_and_load_cfd_by_oracle_event_id() {
let mut conn = setup_test_db().await;
let cfd_1 = Cfd::dummy()
.with_event_id(BitMexPriceEventId::event1())
.insert(&mut conn)
.await;
let cfd_2 = Cfd::dummy()
.with_event_id(BitMexPriceEventId::event1())
.insert(&mut conn)
.await;
let cfd_3 = Cfd::dummy()
.with_event_id(BitMexPriceEventId::event2())
.insert(&mut conn)
.await;
let cfds_event_1 = load_cfds_by_oracle_event_id(BitMexPriceEventId::event1(), &mut conn)
.await
.unwrap();
let cfds_event_2 = load_cfds_by_oracle_event_id(BitMexPriceEventId::event2(), &mut conn)
.await
.unwrap();
assert_eq!(vec![cfd_1, cfd_2], cfds_event_1);
assert_eq!(vec![cfd_3], cfds_event_2);
}
#[tokio::test] #[tokio::test]
async fn test_insert_new_cfd_state_and_load_with_multiple_cfd() { async fn test_insert_new_cfd_state_and_load_with_multiple_cfd() {
let mut conn = setup_test_db().await; let mut conn = setup_test_db().await;
@ -461,43 +351,6 @@ mod tests {
assert_eq!(vec![cfd_1, cfd_2], cfds_from_db); assert_eq!(vec![cfd_1, cfd_2], cfds_from_db);
} }
// test more data; test will add 100 cfds to the database, with each
// having a random number of random updates. Final results are deterministic.
#[tokio::test]
async fn test_multiple_cfd_updates_per_cfd() {
let mut conn = setup_test_db().await;
for i in 0..100 {
let mut cfd = Cfd::dummy()
.with_event_id(BitMexPriceEventId::event1())
.insert(&mut conn)
.await;
let n_updates = rand::thread_rng().gen_range(1, 30);
for _ in 0..n_updates {
cfd.state = random_simple_state();
append_cfd_state(&cfd, &mut conn).await.unwrap();
}
// verify current state is correct
let loaded_by_order_id = load_cfd_by_order_id(cfd.id, &mut conn).await.unwrap();
assert_eq!(loaded_by_order_id, cfd);
// load_cfds_by_oracle_event_id can return multiple CFDs
let loaded_by_oracle_event_id =
load_cfds_by_oracle_event_id(BitMexPriceEventId::event1(), &mut conn)
.await
.unwrap();
assert_eq!(loaded_by_oracle_event_id.len(), i + 1);
}
// verify query returns only one state per CFD
let data = load_all_cfds(&mut conn).await.unwrap();
assert_eq!(data.len(), 100);
}
#[tokio::test] #[tokio::test]
async fn inserting_two_cfds_with_same_order_id_should_fail() { async fn inserting_two_cfds_with_same_order_id_should_fail() {
let mut conn = setup_test_db().await; let mut conn = setup_test_db().await;
@ -514,16 +367,6 @@ mod tests {
); );
} }
fn random_simple_state() -> CfdState {
match rand::thread_rng().gen_range(0, 5) {
0 => CfdState::outgoing_order_request(),
1 => CfdState::accepted(),
2 => CfdState::rejected(),
3 => CfdState::contract_setup(),
_ => CfdState::setup_failed(String::from("dummy failure")),
}
}
async fn setup_test_db() -> PoolConnection<Sqlite> { async fn setup_test_db() -> PoolConnection<Sqlite> {
let pool = SqlitePool::connect(":memory:").await.unwrap(); let pool = SqlitePool::connect(":memory:").await.unwrap();
@ -532,16 +375,6 @@ mod tests {
pool.acquire().await.unwrap() pool.acquire().await.unwrap()
} }
impl BitMexPriceEventId {
fn event1() -> Self {
BitMexPriceEventId::with_20_digits(datetime!(2021-10-13 10:00:00).assume_utc())
}
fn event2() -> Self {
BitMexPriceEventId::with_20_digits(datetime!(2021-10-25 18:00:00).assume_utc())
}
}
impl Cfd { impl Cfd {
fn dummy() -> Self { fn dummy() -> Self {
let (pub_key, _) = Seed::default().derive_identity(); let (pub_key, _) = Seed::default().derive_identity();
@ -561,11 +394,6 @@ mod tests {
self self
} }
fn with_event_id(mut self, id: BitMexPriceEventId) -> Self {
self.oracle_event_id = id;
self
}
} }
impl Order { impl Order {

2
daemon/src/maker_cfd.rs

@ -443,7 +443,7 @@ where
// state // state
let announcement = self let announcement = self
.oracle_actor .oracle_actor
.send(oracle::GetAnnouncement(cfd.oracle_event_id)) .send(oracle::GetAnnouncement(current_order.oracle_event_id))
.await??; .await??;
// 5. Start up contract setup actor // 5. Start up contract setup actor

6
daemon/src/model/cfd.rs

@ -614,11 +614,6 @@ pub struct Cfd {
pub origin: Origin, pub origin: Origin,
/// The id of the event to be used for price attestation
///
/// The maker includes this into the Order based on the Oracle announcement to be used.
pub oracle_event_id: BitMexPriceEventId,
pub fee_rate: u32, pub fee_rate: u32,
pub quantity_usd: Usd, pub quantity_usd: Usd,
@ -643,7 +638,6 @@ impl Cfd {
creation_timestamp: Timestamp::now(), creation_timestamp: Timestamp::now(),
settlement_interval: order.settlement_interval, settlement_interval: order.settlement_interval,
origin: order.origin, origin: order.origin,
oracle_event_id: order.oracle_event_id,
fee_rate: order.fee_rate, fee_rate: order.fee_rate,
counterparty, counterparty,
} }

8
daemon/src/projection.rs

@ -506,8 +506,7 @@ pub struct Cfd {
pub details: CfdDetails, pub details: CfdDetails,
#[serde(with = "::time::serde::timestamp")] pub expiry_timestamp: Option<OffsetDateTime>,
pub expiry_timestamp: OffsetDateTime,
pub counterparty: Identity, pub counterparty: Identity,
} }
@ -560,10 +559,7 @@ impl From<CfdsWithAuxData> for Vec<Cfd> {
margin: cfd.margin().expect("margin to be available"), margin: cfd.margin().expect("margin to be available"),
margin_counterparty: cfd.counterparty_margin().expect("margin to be available"), margin_counterparty: cfd.counterparty_margin().expect("margin to be available"),
details: to_cfd_details(cfd, network), details: to_cfd_details(cfd, network),
expiry_timestamp: match cfd.expiry_timestamp() { expiry_timestamp: cfd.expiry_timestamp(),
None => cfd.oracle_event_id.timestamp(),
Some(timestamp) => timestamp,
},
counterparty: cfd.counterparty.into(), counterparty: cfd.counterparty.into(),
} }
}) })

4
daemon/src/taker_cfd.rs

@ -332,9 +332,9 @@ where
let announcement = self let announcement = self
.oracle_actor .oracle_actor
.send(oracle::GetAnnouncement(cfd.oracle_event_id)) .send(oracle::GetAnnouncement(current_order.oracle_event_id))
.await? .await?
.with_context(|| format!("Announcement {} not found", cfd.oracle_event_id))?; .with_context(|| format!("Announcement {} not found", current_order.oracle_event_id))?;
let this = ctx let this = ctx
.address() .address()

2
maker-frontend/src/components/Types.tsx

@ -49,7 +49,7 @@ export interface Cfd {
actions: Action[]; actions: Action[];
state_transition_timestamp: number; state_transition_timestamp: number;
details: CfdDetails; details: CfdDetails;
expiry_timestamp: number; expiry_timestamp?: number;
counterparty: string; counterparty: string;
} }

4
maker-frontend/src/components/cfdtables/CfdTable.tsx

@ -104,10 +104,10 @@ export function CfdTable(
<VStack> <VStack>
{txs} {txs}
{details.payout && <Box>Payout: {details.payout}</Box>} {details.payout && <Box>Payout: {details.payout}</Box>}
<HStack> {expiry_timestamp && <HStack>
<Text>Expires on:</Text> <Text>Expires on:</Text>
<Timestamp timestamp={expiry_timestamp} /> <Timestamp timestamp={expiry_timestamp} />
</HStack> </HStack>}
</VStack> </VStack>
</Box> </Box>
); );

3
taker-frontend/src/components/CloseButton.tsx

@ -40,7 +40,8 @@ export default function CloseButton({ cfd, request, status, buttonTitle, isForce
<Text> <Text>
This will force close your position with the counterparty. The exchange rate at This will force close your position with the counterparty. The exchange rate at
</Text> </Text>
<Timestamp timestamp={cfd.expiry_timestamp} /> {/*Close button is only available if we have a DLC*/}
<Timestamp timestamp={cfd.expiry_timestamp!} />
<Text> <Text>
will determine your profit/losses. It is likely that the rate will change until then. will determine your profit/losses. It is likely that the rate will change until then.
</Text> </Text>

2
taker-frontend/src/types.ts

@ -58,7 +58,7 @@ export interface Cfd {
state: State; state: State;
state_transition_timestamp: number; state_transition_timestamp: number;
details: CfdDetails; details: CfdDetails;
expiry_timestamp: number; expiry_timestamp?: number;
counterparty: string; counterparty: string;
} }

Loading…
Cancel
Save