From b012aa7a370660828adf9b5bac3350b79a2780c1 Mon Sep 17 00:00:00 2001 From: Philipp Hoenisch Date: Wed, 6 Oct 2021 14:25:14 +1100 Subject: [PATCH] Rollover order boilerplate Add support for multiple update proposals. A taker might request to update/change a CFD in different ways, e.g.: propose to settle it or roll over. In order to support different scenarios we introduce another layer of abstraction. The rest of the changes creates a boilerplate for the actual rollover protocol. --- daemon/src/maker.rs | 10 +- daemon/src/maker_cfd.rs | 118 +++++++++++++++--- daemon/src/model/cfd.rs | 22 +++- daemon/src/routes_maker.rs | 22 +++- daemon/src/routes_taker.rs | 14 ++- daemon/src/taker.rs | 10 +- daemon/src/taker_cfd.rs | 72 +++++++++-- daemon/src/to_sse_event.rs | 45 ++++--- daemon/src/wire.rs | 5 + frontend/src/MakerApp.tsx | 7 ++ frontend/src/components/Types.tsx | 15 +++ .../src/components/cfdtables/CfdTable.tsx | 4 + 12 files changed, 283 insertions(+), 61 deletions(-) diff --git a/daemon/src/maker.rs b/daemon/src/maker.rs index 5935a8c..40be629 100644 --- a/daemon/src/maker.rs +++ b/daemon/src/maker.rs @@ -1,6 +1,6 @@ use crate::auth::MAKER_USERNAME; use crate::db::load_all_cfds; -use crate::model::cfd::SettlementProposals; +use crate::model::cfd::UpdateCfdProposals; use crate::seed::Seed; use crate::wallet::Wallet; use anyhow::{Context, Result}; @@ -113,8 +113,8 @@ async fn main() -> Result<()> { let (order_feed_sender, order_feed_receiver) = watch::channel::>(None); let (wallet_feed_sender, wallet_feed_receiver) = watch::channel::(wallet_info); - let (settlement_feed_sender, settlement_feed_receiver) = - watch::channel::(HashMap::new()); + let (update_cfd_feed_sender, update_cfd_feed_receiver) = + watch::channel::(HashMap::new()); let figment = rocket::Config::figment() .merge(("databases.maker.url", data_dir.join("maker.sqlite"))) @@ -131,7 +131,7 @@ async fn main() -> Result<()> { rocket::custom(figment) .manage(order_feed_receiver) .manage(wallet_feed_receiver) - .manage(settlement_feed_receiver) + .manage(update_cfd_feed_receiver) .manage(auth_password) .manage(quote_updates) .attach(Db::init()) @@ -180,7 +180,7 @@ async fn main() -> Result<()> { schnorrsig::PublicKey::from_keypair(SECP256K1, &oracle), cfd_feed_sender, order_feed_sender, - settlement_feed_sender, + update_cfd_feed_sender, maker_inc_connections_address.clone(), monitor_actor_address.clone(), oracle_actor_address, diff --git a/daemon/src/maker_cfd.rs b/daemon/src/maker_cfd.rs index 02593cf..c58b8b4 100644 --- a/daemon/src/maker_cfd.rs +++ b/daemon/src/maker_cfd.rs @@ -6,11 +6,12 @@ use crate::db::{ use crate::maker_inc_connections::TakerCommand; use crate::model::cfd::{ Cfd, CfdState, CfdStateChangeEvent, CfdStateCommon, Dlc, Order, OrderId, Origin, Role, - SettlementKind, SettlementProposal, SettlementProposals, + RollOverProposal, SettlementKind, SettlementProposal, UpdateCfdProposal, UpdateCfdProposals, }; use crate::model::{OracleEventId, TakerId, Usd}; use crate::monitor::MonitorParams; use crate::wallet::Wallet; +use crate::wire::TakerToMaker; use crate::{maker_inc_connections, monitor, oracle, setup_contract, wire}; use anyhow::{Context as _, Result}; use async_trait::async_trait; @@ -43,6 +44,14 @@ pub struct RejectSettlement { pub order_id: OrderId, } +pub struct AcceptRollOver { + pub order_id: OrderId, +} + +pub struct RejectRollOver { + pub order_id: OrderId, +} + pub struct NewOrder { pub price: Usd, pub min_quantity: Usd, @@ -69,14 +78,14 @@ pub struct Actor { oracle_pk: schnorrsig::PublicKey, cfd_feed_actor_inbox: watch::Sender>, order_feed_sender: watch::Sender>, - settlements_feed_sender: watch::Sender, + update_cfd_feed_sender: watch::Sender, takers: Address, current_order_id: Option, monitor_actor: Address>, setup_state: SetupState, latest_announcements: Option>, oracle_actor: Address>>, - current_settlement_proposals: SettlementProposals, + current_pending_proposals: UpdateCfdProposals, } enum SetupState { @@ -95,7 +104,7 @@ impl Actor { oracle_pk: schnorrsig::PublicKey, cfd_feed_actor_inbox: watch::Sender>, order_feed_sender: watch::Sender>, - settlements_feed_sender: watch::Sender, + update_cfd_feed_sender: watch::Sender, takers: Address, monitor_actor: Address>, oracle_actor: Address>>, @@ -106,21 +115,21 @@ impl Actor { oracle_pk, cfd_feed_actor_inbox, order_feed_sender, - settlements_feed_sender, + update_cfd_feed_sender, takers, current_order_id: None, monitor_actor, setup_state: SetupState::None, latest_announcements: None, oracle_actor, - current_settlement_proposals: HashMap::new(), + current_pending_proposals: HashMap::new(), } } - fn send_current_settlement_proposals(&self) -> Result<()> { + fn send_pending_proposals(&self) -> Result<()> { Ok(self - .settlements_feed_sender - .send(self.current_settlement_proposals.clone())?) + .update_cfd_feed_sender + .send(self.current_pending_proposals.clone())?) } async fn handle_new_order( @@ -189,9 +198,32 @@ impl Actor { "Received settlement proposal from the taker: {:?}", proposal ); - self.current_settlement_proposals - .insert(proposal.order_id, (proposal, SettlementKind::Incoming)); - self.send_current_settlement_proposals()?; + self.current_pending_proposals.insert( + proposal.order_id, + UpdateCfdProposal::Settlement { + proposal, + direction: SettlementKind::Incoming, + }, + ); + self.send_pending_proposals()?; + + Ok(()) + } + + async fn handle_propose_roll_over(&mut self, proposal: RollOverProposal) -> Result<()> { + tracing::info!( + "Received proposal from the taker: {:?} to roll over order {}", + proposal, + proposal.order_id + ); + self.current_pending_proposals.insert( + proposal.order_id, + UpdateCfdProposal::RollOverProposal { + proposal, + direction: SettlementKind::Incoming, + }, + ); + self.send_pending_proposals()?; Ok(()) } @@ -487,10 +519,10 @@ impl Actor { tracing::debug!(%order_id, "Maker accepts a settlement proposal" ); // TODO: Initiate the settlement - self.current_settlement_proposals + self.current_pending_proposals .remove(&order_id) .context("Could not find proposal for given order id")?; - self.send_current_settlement_proposals()?; + self.send_pending_proposals()?; Ok(()) } @@ -499,10 +531,33 @@ impl Actor { // TODO: Handle rejection offer: // - notify the taker that the settlement was rejected - self.current_settlement_proposals + self.current_pending_proposals .remove(&order_id) .context("Could not find proposal for given order id")?; - self.send_current_settlement_proposals()?; + self.send_pending_proposals()?; + Ok(()) + } + + async fn handle_accept_roll_over(&mut self, order_id: OrderId) -> Result<()> { + tracing::debug!(%order_id, "Maker accepts a roll over proposal" ); + + // TODO: Initiate the roll over logic + + self.current_pending_proposals + .remove(&order_id) + .context("Could not find roll over proposal for given order id")?; + self.send_pending_proposals()?; + Ok(()) + } + + async fn handle_reject_roll_over(&mut self, order_id: OrderId) -> Result<()> { + tracing::debug!(%order_id, "Maker rejects a roll over proposal" ); + // TODO: Handle rejection and notify the taker that the roll over was rejected + + self.current_pending_proposals + .remove(&order_id) + .context("Could not find roll over proposal for given order id")?; + self.send_pending_proposals()?; Ok(()) } @@ -588,6 +643,20 @@ impl Handler for Actor { } } +#[async_trait] +impl Handler for Actor { + async fn handle(&mut self, msg: AcceptRollOver, _ctx: &mut Context) { + log_error!(self.handle_accept_roll_over(msg.order_id)) + } +} + +#[async_trait] +impl Handler for Actor { + async fn handle(&mut self, msg: RejectRollOver, _ctx: &mut Context) { + log_error!(self.handle_reject_roll_over(msg.order_id)) + } +} + #[async_trait] impl Handler for Actor { async fn handle(&mut self, msg: Commit, _ctx: &mut Context) { @@ -662,6 +731,15 @@ impl Handler for Actor { wire::TakerToMaker::Protocol(msg) => { log_error!(self.handle_inc_protocol_msg(taker, msg)) } + TakerToMaker::ProposeRollOver { + order_id, + timestamp, + } => { + log_error!(self.handle_propose_roll_over(RollOverProposal { + order_id, + timestamp, + })) + } } KeepRunning::Yes @@ -714,6 +792,14 @@ impl Message for RejectSettlement { type Result = (); } +impl Message for AcceptRollOver { + type Result = (); +} + +impl Message for RejectRollOver { + type Result = (); +} + // this signature is a bit different because we use `Address::attach_stream` impl Message for TakerStreamMessage { type Result = KeepRunning; diff --git a/daemon/src/model/cfd.rs b/daemon/src/model/cfd.rs index af574d2..3818853 100644 --- a/daemon/src/model/cfd.rs +++ b/daemon/src/model/cfd.rs @@ -345,6 +345,18 @@ impl fmt::Display for CfdState { } } +#[derive(Debug, Clone)] +pub enum UpdateCfdProposal { + Settlement { + proposal: SettlementProposal, + direction: SettlementKind, + }, + RollOverProposal { + proposal: RollOverProposal, + direction: SettlementKind, + }, +} + /// Proposed collaborative settlement #[derive(Debug, Clone)] pub struct SettlementProposal { @@ -354,6 +366,14 @@ pub struct SettlementProposal { pub maker: Amount, } +/// Proposal to roll over over a fixed length. +/// The length of the roll over is defined by the maker. +#[derive(Debug, Clone)] +pub struct RollOverProposal { + pub order_id: OrderId, + pub timestamp: SystemTime, +} + #[derive(Debug, Clone)] #[allow(dead_code)] // Variants (for now) used by different binaries. pub enum SettlementKind { @@ -361,7 +381,7 @@ pub enum SettlementKind { Outgoing, } -pub type SettlementProposals = HashMap; +pub type UpdateCfdProposals = HashMap; /// Represents a cfd (including state) #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] diff --git a/daemon/src/routes_maker.rs b/daemon/src/routes_maker.rs index 409b989..ab146a2 100644 --- a/daemon/src/routes_maker.rs +++ b/daemon/src/routes_maker.rs @@ -1,5 +1,5 @@ use crate::auth::Authenticated; -use crate::model::cfd::{Cfd, Order, OrderId, Role, SettlementProposals}; +use crate::model::cfd::{Cfd, Order, OrderId, Role, UpdateCfdProposals}; use crate::model::{Usd, WalletInfo}; use crate::routes::EmbeddedFileExt; use crate::to_sse_event::{CfdAction, CfdsWithAuxData, ToSseEvent}; @@ -24,7 +24,7 @@ pub async fn maker_feed( rx_order: &State>>, rx_wallet: &State>, rx_quote: &State>, - rx_settlements: &State>, + rx_settlements: &State>, _auth: Authenticated, ) -> EventStream![] { let mut rx_cfds = rx_cfds.inner().clone(); @@ -149,6 +149,19 @@ pub async fn post_cfd_action( .await .expect("actor to always be available"); } + + CfdAction::AcceptRollOver => { + cfd_actor_address + .do_send_async(maker_cfd::AcceptRollOver { order_id: id }) + .await + .expect("actor to always be available"); + } + CfdAction::RejectRollOver => { + cfd_actor_address + .do_send_async(maker_cfd::RejectRollOver { order_id: id }) + .await + .expect("actor to always be available"); + } CfdAction::Commit => { cfd_actor_address .do_send_async(maker_cfd::Commit { order_id: id }) @@ -160,6 +173,11 @@ pub async fn post_cfd_action( "Collaborative settlement can only be triggered by taker".to_string(), ))); } + CfdAction::RollOver => { + return Err(status::BadRequest(Some( + "RollOver proposal can only be triggered by taker".to_string(), + ))); + } } Ok(status::Accepted(None)) diff --git a/daemon/src/routes_taker.rs b/daemon/src/routes_taker.rs index 2afdb40..77e49d2 100644 --- a/daemon/src/routes_taker.rs +++ b/daemon/src/routes_taker.rs @@ -1,4 +1,4 @@ -use crate::model::cfd::{calculate_buy_margin, Cfd, Order, OrderId, Role, SettlementProposals}; +use crate::model::cfd::{calculate_buy_margin, Cfd, Order, OrderId, Role, UpdateCfdProposals}; use crate::model::{Leverage, Usd, WalletInfo}; use crate::routes::EmbeddedFileExt; use crate::to_sse_event::{CfdAction, CfdsWithAuxData, ToSseEvent}; @@ -23,7 +23,7 @@ pub async fn feed( rx_order: &State>>, rx_wallet: &State>, rx_quote: &State>, - rx_settlements: &State>, + rx_settlements: &State>, ) -> EventStream![] { let mut rx_cfds = rx_cfds.inner().clone(); let mut rx_order = rx_order.inner().clone(); @@ -100,7 +100,9 @@ pub async fn post_cfd_action( CfdAction::AcceptOrder | CfdAction::RejectOrder | CfdAction::AcceptSettlement - | CfdAction::RejectSettlement => { + | CfdAction::RejectSettlement + | CfdAction::AcceptRollOver + | CfdAction::RejectRollOver => { return Err(status::BadRequest(None)); } CfdAction::Commit => { @@ -119,6 +121,12 @@ pub async fn post_cfd_action( .await .expect("actor to always be available"); } + CfdAction::RollOver => { + cfd_actor_address + .do_send_async(taker_cfd::ProposeRollOver { order_id: id }) + .await + .expect("actor to always be available"); + } } Ok(status::Accepted(None)) diff --git a/daemon/src/taker.rs b/daemon/src/taker.rs index 940e1cf..7fc4412 100644 --- a/daemon/src/taker.rs +++ b/daemon/src/taker.rs @@ -1,5 +1,5 @@ use crate::db::load_all_cfds; -use crate::model::cfd::SettlementProposals; +use crate::model::cfd::UpdateCfdProposals; use crate::model::WalletInfo; use crate::wallet::Wallet; use anyhow::{Context, Result}; @@ -108,8 +108,8 @@ async fn main() -> Result<()> { let (order_feed_sender, order_feed_receiver) = watch::channel::>(None); let (wallet_feed_sender, wallet_feed_receiver) = watch::channel::(wallet_info); - let (settlement_feed_sender, settlement_feed_receiver) = - watch::channel::(HashMap::new()); + let (update_cfd_feed_sender, update_feed_receiver) = + watch::channel::(HashMap::new()); let (read, write) = loop { let socket = tokio::net::TcpSocket::new_v4()?; @@ -134,7 +134,7 @@ async fn main() -> Result<()> { rocket::custom(figment) .manage(order_feed_receiver) .manage(wallet_feed_receiver) - .manage(settlement_feed_receiver) + .manage(update_feed_receiver) .manage(quote_updates) .attach(Db::init()) .attach(AdHoc::try_on_ignite( @@ -182,7 +182,7 @@ async fn main() -> Result<()> { schnorrsig::PublicKey::from_keypair(SECP256K1, &oracle), cfd_feed_sender, order_feed_sender, - settlement_feed_sender, + update_cfd_feed_sender, send_to_maker, monitor_actor_address.clone(), oracle_actor_address, diff --git a/daemon/src/taker_cfd.rs b/daemon/src/taker_cfd.rs index 95a22c2..4e1a7ee 100644 --- a/daemon/src/taker_cfd.rs +++ b/daemon/src/taker_cfd.rs @@ -5,7 +5,7 @@ use crate::db::{ }; use crate::model::cfd::{ Cfd, CfdState, CfdStateChangeEvent, CfdStateCommon, Dlc, Order, OrderId, Origin, Role, - SettlementKind, SettlementProposals, + RollOverProposal, SettlementKind, UpdateCfdProposal, UpdateCfdProposals, }; use crate::model::{OracleEventId, Usd}; use crate::monitor::{self, MonitorParams}; @@ -33,6 +33,10 @@ pub struct ProposeSettlement { pub current_price: Usd, } +pub struct ProposeRollOver { + pub order_id: OrderId, +} + pub struct MakerStreamMessage { pub item: Result, } @@ -59,13 +63,13 @@ pub struct Actor { oracle_pk: schnorrsig::PublicKey, cfd_feed_actor_inbox: watch::Sender>, order_feed_actor_inbox: watch::Sender>, - settlements_feed_sender: watch::Sender, + update_cfd_feed_sender: watch::Sender, send_to_maker: Address>, monitor_actor: Address>, setup_state: SetupState, latest_announcements: Option>, oracle_actor: Address>>, - current_settlement_proposals: SettlementProposals, + current_pending_proposals: UpdateCfdProposals, } impl Actor { @@ -76,7 +80,7 @@ impl Actor { oracle_pk: schnorrsig::PublicKey, cfd_feed_actor_inbox: watch::Sender>, order_feed_actor_inbox: watch::Sender>, - settlements_feed_sender: watch::Sender, + update_cfd_feed_sender: watch::Sender, send_to_maker: Address>, monitor_actor: Address>, oracle_actor: Address>>, @@ -87,20 +91,20 @@ impl Actor { oracle_pk, cfd_feed_actor_inbox, order_feed_actor_inbox, - settlements_feed_sender, + update_cfd_feed_sender, send_to_maker, monitor_actor, setup_state: SetupState::None, oracle_actor, latest_announcements: None, - current_settlement_proposals: HashMap::new(), + current_pending_proposals: HashMap::new(), } } - fn send_current_settlement_proposals(&self) -> Result<()> { + fn send_pending_update_proposals(&self) -> Result<()> { Ok(self - .settlements_feed_sender - .send(self.current_settlement_proposals.clone())?) + .update_cfd_feed_sender + .send(self.current_pending_proposals.clone())?) } async fn handle_take_offer(&mut self, order_id: OrderId, quantity: Usd) -> Result<()> { @@ -142,7 +146,7 @@ impl Actor { let proposal = cfd.calculate_settlement(current_price)?; if self - .current_settlement_proposals + .current_pending_proposals .contains_key(&proposal.order_id) { anyhow::bail!( @@ -151,11 +155,14 @@ impl Actor { ) } - self.current_settlement_proposals.insert( + self.current_pending_proposals.insert( proposal.order_id, - (proposal.clone(), SettlementKind::Outgoing), + UpdateCfdProposal::Settlement { + proposal: proposal.clone(), + direction: SettlementKind::Outgoing, + }, ); - self.send_current_settlement_proposals()?; + self.send_pending_update_proposals()?; self.send_to_maker .do_send_async(wire::TakerToMaker::ProposeSettlement { @@ -168,6 +175,34 @@ impl Actor { Ok(()) } + async fn handle_propose_roll_over(&mut self, order_id: OrderId) -> Result<()> { + if self.current_pending_proposals.contains_key(&order_id) { + anyhow::bail!("An update for order id {} is already in progress", order_id) + } + + let proposal = RollOverProposal { + order_id, + timestamp: SystemTime::now(), + }; + + self.current_pending_proposals.insert( + proposal.order_id, + UpdateCfdProposal::RollOverProposal { + proposal: proposal.clone(), + direction: SettlementKind::Outgoing, + }, + ); + self.send_pending_update_proposals()?; + + self.send_to_maker + .do_send_async(wire::TakerToMaker::ProposeRollOver { + order_id: proposal.order_id, + timestamp: proposal.timestamp, + }) + .await?; + Ok(()) + } + async fn handle_new_order(&mut self, order: Option) -> Result<()> { match order { Some(mut order) => { @@ -432,6 +467,13 @@ impl Handler for Actor { } } +#[async_trait] +impl Handler for Actor { + async fn handle(&mut self, msg: ProposeRollOver, _ctx: &mut Context) { + log_error!(self.handle_propose_roll_over(msg.order_id)); + } +} + #[async_trait] impl Handler for Actor { async fn handle( @@ -510,6 +552,10 @@ impl Message for ProposeSettlement { type Result = (); } +impl Message for ProposeRollOver { + type Result = (); +} + // this signature is a bit different because we use `Address::attach_stream` impl Message for MakerStreamMessage { type Result = KeepRunning; diff --git a/daemon/src/to_sse_event.rs b/daemon/src/to_sse_event.rs index dce8895..38974db 100644 --- a/daemon/src/to_sse_event.rs +++ b/daemon/src/to_sse_event.rs @@ -1,4 +1,4 @@ -use crate::model::cfd::{OrderId, Role, SettlementKind, SettlementProposals}; +use crate::model::cfd::{OrderId, Role, SettlementKind, UpdateCfdProposal, UpdateCfdProposals}; use crate::model::{Leverage, Position, TradingPair, Usd}; use crate::{bitmex_price_feed, model}; use bdk::bitcoin::{Amount, SignedAmount}; @@ -42,6 +42,9 @@ pub enum CfdAction { Settle, AcceptSettlement, RejectSettlement, + RollOver, + AcceptRollOver, + RejectRollOver, } impl<'v> FromParam<'v> for CfdAction { @@ -66,6 +69,8 @@ pub enum CfdState { OpenCommitted, IncomingSettlementProposal, OutgoingSettlementProposal, + IncomingRollOverProposal, + OutgoingRollOverProposal, MustRefund, Refunded, SetupFailed, @@ -100,14 +105,14 @@ pub trait ToSseEvent { pub struct CfdsWithAuxData { pub cfds: Vec, pub current_price: Usd, - pub settlement_proposals: SettlementProposals, + pub pending_proposals: UpdateCfdProposals, } impl CfdsWithAuxData { pub fn new( rx_cfds: &watch::Receiver>, rx_quote: &watch::Receiver, - rx_settlement: &watch::Receiver, + rx_updates: &watch::Receiver, role: Role, ) -> Self { let quote = rx_quote.borrow().clone(); @@ -116,21 +121,14 @@ impl CfdsWithAuxData { Role::Taker => quote.for_taker(), }; - let settlement_proposals = rx_settlement.borrow().clone(); + let pending_proposals = rx_updates.borrow().clone(); CfdsWithAuxData { cfds: rx_cfds.borrow().clone(), current_price, - settlement_proposals, + pending_proposals, } } - - /// Check whether given CFD has any active settlement proposals - fn settlement_proposal_status(&self, cfd: &model::cfd::Cfd) -> Option { - self.settlement_proposals - .get(&cfd.order.id) - .map(|(_, kind)| kind.clone()) - } } impl ToSseEvent for CfdsWithAuxData { @@ -151,7 +149,8 @@ impl ToSseEvent for CfdsWithAuxData { (SignedAmount::ZERO, Decimal::ZERO.into()) }); - let state = to_cfd_state(&cfd.state, self.settlement_proposal_status(cfd)); + let pending_proposal = self.pending_proposals.get(&cfd.order.id); + let state = to_cfd_state(&cfd.state, pending_proposal); Cfd { order_id: cfd.order.id, @@ -228,11 +227,17 @@ impl ToSseEvent for model::WalletInfo { fn to_cfd_state( cfd_state: &model::cfd::CfdState, - proposal_status: Option, + proposal_status: Option<&UpdateCfdProposal>, ) -> CfdState { match proposal_status { - Some(SettlementKind::Incoming) => CfdState::IncomingSettlementProposal, - Some(SettlementKind::Outgoing) => CfdState::OutgoingSettlementProposal, + Some(UpdateCfdProposal::Settlement { + direction: SettlementKind::Outgoing, + .. + }) => CfdState::OutgoingSettlementProposal, + Some(UpdateCfdProposal::Settlement { + direction: SettlementKind::Incoming, + .. + }) => CfdState::IncomingSettlementProposal, None => match cfd_state { model::cfd::CfdState::OutgoingOrderRequest { .. } => CfdState::OutgoingOrderRequest, model::cfd::CfdState::IncomingOrderRequest { .. } => CfdState::IncomingOrderRequest, @@ -247,6 +252,14 @@ fn to_cfd_state( model::cfd::CfdState::SetupFailed { .. } => CfdState::SetupFailed, model::cfd::CfdState::PendingCommit { .. } => CfdState::PendingCommit, }, + Some(UpdateCfdProposal::RollOverProposal { + direction: SettlementKind::Outgoing, + .. + }) => CfdState::OutgoingRollOverProposal, + Some(UpdateCfdProposal::RollOverProposal { + direction: SettlementKind::Incoming, + .. + }) => CfdState::IncomingRollOverProposal, } } diff --git a/daemon/src/wire.rs b/daemon/src/wire.rs index 1c11789..e0f5dd2 100644 --- a/daemon/src/wire.rs +++ b/daemon/src/wire.rs @@ -33,6 +33,10 @@ pub enum TakerToMaker { #[serde(with = "::bdk::bitcoin::util::amount::serde::as_btc")] maker: Amount, }, + ProposeRollOver { + order_id: OrderId, + timestamp: SystemTime, + }, Protocol(SetupMsg), } @@ -42,6 +46,7 @@ impl fmt::Display for TakerToMaker { TakerToMaker::TakeOrder { .. } => write!(f, "TakeOrder"), TakerToMaker::ProposeSettlement { .. } => write!(f, "ProposeSettlement"), TakerToMaker::Protocol(_) => write!(f, "Protocol"), + TakerToMaker::ProposeRollOver { .. } => write!(f, "ProposeRollOver"), } } } diff --git a/frontend/src/MakerApp.tsx b/frontend/src/MakerApp.tsx index 40dd64e..5cc3dd4 100644 --- a/frontend/src/MakerApp.tsx +++ b/frontend/src/MakerApp.tsx @@ -68,6 +68,9 @@ export default function App() { const acceptOrRejectSettlement = cfds.filter((value) => value.state.getGroup() === StateGroupKey.ACCEPT_OR_REJECT_SETTLEMENT ); + const acceptOrRejectRollOvers = cfds.filter((value) => + value.state.getGroup() === StateGroupKey.ACCEPT_OR_REJECT_ROLL_OVER + ); const opening = cfds.filter((value) => value.state.getGroup() === StateGroupKey.OPENING); const open = cfds.filter((value) => value.state.getGroup() === StateGroupKey.OPEN); const closed = cfds.filter((value) => value.state.getGroup() === StateGroupKey.CLOSED); @@ -161,6 +164,7 @@ export default function App() { Open [{open.length}] Accept / Reject Order [{acceptOrRejectOrder.length}] Accept / Reject Settlement [{acceptOrRejectSettlement.length}] + Accept / Reject Roll Overs [{acceptOrRejectRollOvers.length}] Opening [{opening.length}] Closed [{closed.length}] @@ -175,6 +179,9 @@ export default function App() { + + + diff --git a/frontend/src/components/Types.tsx b/frontend/src/components/Types.tsx index d261019..0161752 100644 --- a/frontend/src/components/Types.tsx +++ b/frontend/src/components/Types.tsx @@ -77,6 +77,10 @@ export class State { return "Settlement Proposed"; case StateKey.OUTGOING_SETTLEMENT_PROPOSAL: return "Settlement Proposed"; + case StateKey.INCOMING_ROLL_OVER_PROPOSAL: + return "Rollover Proposed"; + case StateKey.OUTGOING_ROLL_OVER_PROPOSAL: + return "Rollover Proposed"; case StateKey.MUST_REFUND: return "Refunding"; case StateKey.REFUNDED: @@ -109,6 +113,8 @@ export class State { case StateKey.INCOMING_ORDER_REQUEST: case StateKey.OUTGOING_SETTLEMENT_PROPOSAL: case StateKey.INCOMING_SETTLEMENT_PROPOSAL: + case StateKey.INCOMING_ROLL_OVER_PROPOSAL: + case StateKey.OUTGOING_ROLL_OVER_PROPOSAL: case StateKey.CONTRACT_SETUP: case StateKey.PENDING_OPEN: case StateKey.REFUNDED: @@ -133,11 +139,15 @@ export class State { case StateKey.OPEN_COMMITTED: case StateKey.MUST_REFUND: case StateKey.OUTGOING_SETTLEMENT_PROPOSAL: + case StateKey.OUTGOING_ROLL_OVER_PROPOSAL: return StateGroupKey.OPEN; case StateKey.INCOMING_SETTLEMENT_PROPOSAL: return StateGroupKey.ACCEPT_OR_REJECT_SETTLEMENT; + case StateKey.INCOMING_ROLL_OVER_PROPOSAL: + return StateGroupKey.ACCEPT_OR_REJECT_ROLL_OVER; + case StateKey.REJECTED: case StateKey.REFUNDED: case StateKey.SETUP_FAILED: @@ -153,6 +163,8 @@ export enum Action { SETTLE = "settle", ACCEPT_SETTLEMENT = "acceptSettlement", REJECT_SETTLEMENT = "rejectSettlement", + ACCEPT_ROLL_OVER = "acceptRollOver", + REJECT_ROLL_OVER = "rejectRollOver", } const enum StateKey { @@ -167,6 +179,8 @@ const enum StateKey { OPEN_COMMITTED = "OpenCommitted", OUTGOING_SETTLEMENT_PROPOSAL = "OutgoingSettlementProposal", INCOMING_SETTLEMENT_PROPOSAL = "IncomingSettlementProposal", + OUTGOING_ROLL_OVER_PROPOSAL = "OutgoingRollOverProposal", + INCOMING_ROLL_OVER_PROPOSAL = "IncomingRollOverProposal", MUST_REFUND = "MustRefund", REFUNDED = "Refunded", SETUP_FAILED = "SetupFailed", @@ -179,6 +193,7 @@ export enum StateGroupKey { /// A CFD that is an ongoing open position (on chain) OPEN = "Open", ACCEPT_OR_REJECT_SETTLEMENT = "Accept / Reject Settlement", + ACCEPT_OR_REJECT_ROLL_OVER = "Accept / Reject Roll Over", /// A CFD that has been successfully or not-successfully terminated CLOSED = "Closed", } diff --git a/frontend/src/components/cfdtables/CfdTable.tsx b/frontend/src/components/cfdtables/CfdTable.tsx index 4c58b92..57818a1 100644 --- a/frontend/src/components/cfdtables/CfdTable.tsx +++ b/frontend/src/components/cfdtables/CfdTable.tsx @@ -216,6 +216,10 @@ function colorSchemaForAction(action: Action): string { return "green"; case Action.REJECT_SETTLEMENT: return "red"; + case Action.ACCEPT_ROLL_OVER: + return "green"; + case Action.REJECT_ROLL_OVER: + return "red"; } }