Browse Source

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.
feature/integration-tests
Philipp Hoenisch 3 years ago
parent
commit
b012aa7a37
No known key found for this signature in database GPG Key ID: E5F8E74C672BC666
  1. 10
      daemon/src/maker.rs
  2. 118
      daemon/src/maker_cfd.rs
  3. 22
      daemon/src/model/cfd.rs
  4. 22
      daemon/src/routes_maker.rs
  5. 14
      daemon/src/routes_taker.rs
  6. 10
      daemon/src/taker.rs
  7. 72
      daemon/src/taker_cfd.rs
  8. 45
      daemon/src/to_sse_event.rs
  9. 5
      daemon/src/wire.rs
  10. 7
      frontend/src/MakerApp.tsx
  11. 15
      frontend/src/components/Types.tsx
  12. 4
      frontend/src/components/cfdtables/CfdTable.tsx

10
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::<Option<Order>>(None);
let (wallet_feed_sender, wallet_feed_receiver) = watch::channel::<WalletInfo>(wallet_info);
let (settlement_feed_sender, settlement_feed_receiver) =
watch::channel::<SettlementProposals>(HashMap::new());
let (update_cfd_feed_sender, update_cfd_feed_receiver) =
watch::channel::<UpdateCfdProposals>(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,

118
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<Vec<Cfd>>,
order_feed_sender: watch::Sender<Option<Order>>,
settlements_feed_sender: watch::Sender<SettlementProposals>,
update_cfd_feed_sender: watch::Sender<UpdateCfdProposals>,
takers: Address<maker_inc_connections::Actor>,
current_order_id: Option<OrderId>,
monitor_actor: Address<monitor::Actor<Actor>>,
setup_state: SetupState,
latest_announcements: Option<BTreeMap<OracleEventId, oracle::Announcement>>,
oracle_actor: Address<oracle::Actor<Actor, monitor::Actor<Actor>>>,
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<Vec<Cfd>>,
order_feed_sender: watch::Sender<Option<Order>>,
settlements_feed_sender: watch::Sender<SettlementProposals>,
update_cfd_feed_sender: watch::Sender<UpdateCfdProposals>,
takers: Address<maker_inc_connections::Actor>,
monitor_actor: Address<monitor::Actor<Actor>>,
oracle_actor: Address<oracle::Actor<Actor, monitor::Actor<Actor>>>,
@ -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<RejectSettlement> for Actor {
}
}
#[async_trait]
impl Handler<AcceptRollOver> for Actor {
async fn handle(&mut self, msg: AcceptRollOver, _ctx: &mut Context<Self>) {
log_error!(self.handle_accept_roll_over(msg.order_id))
}
}
#[async_trait]
impl Handler<RejectRollOver> for Actor {
async fn handle(&mut self, msg: RejectRollOver, _ctx: &mut Context<Self>) {
log_error!(self.handle_reject_roll_over(msg.order_id))
}
}
#[async_trait]
impl Handler<Commit> for Actor {
async fn handle(&mut self, msg: Commit, _ctx: &mut Context<Self>) {
@ -662,6 +731,15 @@ impl Handler<TakerStreamMessage> 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;

22
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<OrderId, (SettlementProposal, SettlementKind)>;
pub type UpdateCfdProposals = HashMap<OrderId, UpdateCfdProposal>;
/// Represents a cfd (including state)
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]

22
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<watch::Receiver<Option<Order>>>,
rx_wallet: &State<watch::Receiver<WalletInfo>>,
rx_quote: &State<watch::Receiver<bitmex_price_feed::Quote>>,
rx_settlements: &State<watch::Receiver<SettlementProposals>>,
rx_settlements: &State<watch::Receiver<UpdateCfdProposals>>,
_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))

14
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<watch::Receiver<Option<Order>>>,
rx_wallet: &State<watch::Receiver<WalletInfo>>,
rx_quote: &State<watch::Receiver<bitmex_price_feed::Quote>>,
rx_settlements: &State<watch::Receiver<SettlementProposals>>,
rx_settlements: &State<watch::Receiver<UpdateCfdProposals>>,
) -> 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))

10
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::<Option<Order>>(None);
let (wallet_feed_sender, wallet_feed_receiver) = watch::channel::<WalletInfo>(wallet_info);
let (settlement_feed_sender, settlement_feed_receiver) =
watch::channel::<SettlementProposals>(HashMap::new());
let (update_cfd_feed_sender, update_feed_receiver) =
watch::channel::<UpdateCfdProposals>(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,

72
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<wire::MakerToTaker>,
}
@ -59,13 +63,13 @@ pub struct Actor {
oracle_pk: schnorrsig::PublicKey,
cfd_feed_actor_inbox: watch::Sender<Vec<Cfd>>,
order_feed_actor_inbox: watch::Sender<Option<Order>>,
settlements_feed_sender: watch::Sender<SettlementProposals>,
update_cfd_feed_sender: watch::Sender<UpdateCfdProposals>,
send_to_maker: Address<send_to_socket::Actor<wire::TakerToMaker>>,
monitor_actor: Address<monitor::Actor<Actor>>,
setup_state: SetupState,
latest_announcements: Option<BTreeMap<OracleEventId, oracle::Announcement>>,
oracle_actor: Address<oracle::Actor<Actor, monitor::Actor<Actor>>>,
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<Vec<Cfd>>,
order_feed_actor_inbox: watch::Sender<Option<Order>>,
settlements_feed_sender: watch::Sender<SettlementProposals>,
update_cfd_feed_sender: watch::Sender<UpdateCfdProposals>,
send_to_maker: Address<send_to_socket::Actor<wire::TakerToMaker>>,
monitor_actor: Address<monitor::Actor<Actor>>,
oracle_actor: Address<oracle::Actor<Actor, monitor::Actor<Actor>>>,
@ -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<Order>) -> Result<()> {
match order {
Some(mut order) => {
@ -432,6 +467,13 @@ impl Handler<ProposeSettlement> for Actor {
}
}
#[async_trait]
impl Handler<ProposeRollOver> for Actor {
async fn handle(&mut self, msg: ProposeRollOver, _ctx: &mut Context<Self>) {
log_error!(self.handle_propose_roll_over(msg.order_id));
}
}
#[async_trait]
impl Handler<MakerStreamMessage> 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;

45
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<model::cfd::Cfd>,
pub current_price: Usd,
pub settlement_proposals: SettlementProposals,
pub pending_proposals: UpdateCfdProposals,
}
impl CfdsWithAuxData {
pub fn new(
rx_cfds: &watch::Receiver<Vec<model::cfd::Cfd>>,
rx_quote: &watch::Receiver<bitmex_price_feed::Quote>,
rx_settlement: &watch::Receiver<SettlementProposals>,
rx_updates: &watch::Receiver<UpdateCfdProposals>,
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<SettlementKind> {
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<SettlementKind>,
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,
}
}

5
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"),
}
}
}

7
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() {
<Tab>Open [{open.length}]</Tab>
<Tab>Accept / Reject Order [{acceptOrRejectOrder.length}]</Tab>
<Tab>Accept / Reject Settlement [{acceptOrRejectSettlement.length}]</Tab>
<Tab>Accept / Reject Roll Overs [{acceptOrRejectRollOvers.length}]</Tab>
<Tab>Opening [{opening.length}]</Tab>
<Tab>Closed [{closed.length}]</Tab>
</TabList>
@ -175,6 +179,9 @@ export default function App() {
<TabPanel>
<CfdTable data={acceptOrRejectSettlement} />
</TabPanel>
<TabPanel>
<CfdTable data={acceptOrRejectRollOvers} />
</TabPanel>
<TabPanel>
<CfdTable data={opening} />
</TabPanel>

15
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",
}

4
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";
}
}

Loading…
Cancel
Save