diff --git a/daemon/src/projection.rs b/daemon/src/projection.rs index a01ad1d..d151e64 100644 --- a/daemon/src/projection.rs +++ b/daemon/src/projection.rs @@ -488,9 +488,9 @@ pub struct Cfd { #[serde(with = "::bdk::bitcoin::util::amount::serde::as_btc")] pub margin_counterparty: Amount, - #[serde(with = "::bdk::bitcoin::util::amount::serde::as_btc")] - pub profit_btc: SignedAmount, - pub profit_percent: String, + #[serde(with = "::bdk::bitcoin::util::amount::serde::as_btc::opt")] + pub profit_btc: Option, + pub profit_percent: Option, pub state: CfdState, pub actions: Vec, @@ -511,14 +511,17 @@ impl From for Vec { .cfds .iter() .map(|cfd| { - let (profit_btc, profit_percent) = - cfd.profit(current_price).unwrap_or_else(|error| { - tracing::warn!( - "Calculating profit/loss failed. Falling back to 0. {:#}", - error - ); - (SignedAmount::ZERO, Decimal::ZERO.into()) - }); + let (profit_btc, profit_percent) = match cfd.profit(current_price) { + Ok((profit_btc, profit_percent)) => ( + Some(profit_btc), + Some(profit_percent.round_dp(1).to_string()), + ), + Err(e) => { + tracing::warn!("Failed to calculate profit/loss {:#}", e); + + (None, None) + } + }; let pending_proposal = input.pending_proposals.get(&cfd.order.id); let state = to_cfd_state(&cfd.state, pending_proposal); @@ -532,7 +535,7 @@ impl From for Vec { liquidation_price: cfd.order.liquidation_price.into(), quantity_usd: cfd.quantity_usd.into(), profit_btc, - profit_percent: profit_percent.round_dp(1).to_string(), + profit_percent, state: state.clone(), actions: available_actions(state, cfd.role()), state_transition_timestamp: cfd.state.get_transition_timestamp().seconds(), diff --git a/taker-frontend/src/components/History.tsx b/taker-frontend/src/components/History.tsx index af2f2fb..3f9ccb8 100644 --- a/taker-frontend/src/components/History.tsx +++ b/taker-frontend/src/components/History.tsx @@ -8,6 +8,7 @@ import { HStack, Link, SimpleGrid, + Skeleton, Spinner, Table, Tbody, @@ -60,11 +61,6 @@ const CfdDetails = ({ cfd, connectedToMaker }: CfdDetailsProps) => { const margin = `₿${Math.round((cfd.margin) * 1_000_000) / 1_000_000}`; const liquidationPrice = `$${cfd.liquidation_price}`; - const pAndLNumber = Math.round((cfd.profit_btc) * 1_000_000) / 1_000_000; - const pAndL = pAndLNumber < 0 ? `-₿${Math.abs(pAndLNumber)}` : `₿${Math.abs(pAndLNumber)}`; - - const payout = `₿${Math.round((cfd.margin + cfd.profit_btc) * 1_000_000) / 1_000_000}`; - const txLock = cfd.details.tx_url_list.find((tx) => tx.label === TxLabel.Lock); const txCommit = cfd.details.tx_url_list.find((tx) => tx.label === TxLabel.Commit); const txRefund = cfd.details.tx_url_list.find((tx) => tx.label === TxLabel.Refund); @@ -102,11 +98,19 @@ const CfdDetails = ({ cfd, connectedToMaker }: CfdDetailsProps) => { Unrealized P/L - {pAndL} + + + + + Payout - {payout} + + + + + @@ -158,6 +162,33 @@ const CfdDetails = ({ cfd, connectedToMaker }: CfdDetailsProps) => { ); }; +interface ProfitAndLossProps { + profitBtc: number; +} + +function ProfitAndLoss({ profitBtc }: ProfitAndLossProps) { + const pAndLNumber = Math.round((profitBtc) * 1_000_000) / 1_000_000; + const absPAndL = Math.abs(pAndLNumber); + const negativeSign = pAndLNumber < 0 ? "-" : ""; + + return + {negativeSign}₿{absPAndL} + ; +} + +interface PayoutProps { + profitBtc: number; + margin: number; +} + +function Payout({ profitBtc, margin }: PayoutProps) { + let payoutBtc = Math.round((margin + profitBtc) * 1_000_000) / 1_000_000; + + return + ₿{payoutBtc} + ; +} + const CircleIcon = (props: any) => (