diff --git a/coordinator/src/node/channel_opening_fee.rs b/coordinator/src/node/channel_opening_fee.rs index d158e2e0e..e15809415 100644 --- a/coordinator/src/node/channel_opening_fee.rs +++ b/coordinator/src/node/channel_opening_fee.rs @@ -7,20 +7,19 @@ use bitcoin::secp256k1::ThirtyTwoByteHash; use lightning::ln::PaymentHash; use lightning_invoice::Invoice; use ln_dlc_node::PaymentInfo; +use orderbook_commons::JIT_FEE_INVOICE_DESCRIPTION_PREFIX; impl Node { pub async fn channel_opening_fee_invoice( &self, amount: u64, funding_txid: String, - description: Option, expiry: Option, ) -> Result { - let invoice = self.inner.create_invoice( - amount, - description.unwrap_or_default(), - expiry.unwrap_or(180), - )?; + let description = format!("{JIT_FEE_INVOICE_DESCRIPTION_PREFIX} {funding_txid}"); + let invoice = self + .inner + .create_invoice(amount, description, expiry.unwrap_or(180))?; let payment_hash = invoice.payment_hash().into_32(); let payment_hash_hex = payment_hash.to_hex(); diff --git a/coordinator/src/node/order_matching_fee.rs b/coordinator/src/node/order_matching_fee.rs index 89da27513..3c300927b 100644 --- a/coordinator/src/node/order_matching_fee.rs +++ b/coordinator/src/node/order_matching_fee.rs @@ -19,7 +19,7 @@ impl Node { trade_params: &TradeParams, ) -> Result<(PaymentHash, Invoice)> { let order_id = trade_params.filled_with.order_id; - let description = format!("{FEE_INVOICE_DESCRIPTION_PREFIX_TAKER}{order_id}"); + let description = format!("{FEE_INVOICE_DESCRIPTION_PREFIX_TAKER} {order_id}"); let fee = order_matching_fee_taker( trade_params.quantity, diff --git a/coordinator/src/routes.rs b/coordinator/src/routes.rs index b7b529d1e..2cc4ad539 100644 --- a/coordinator/src/routes.rs +++ b/coordinator/src/routes.rs @@ -202,7 +202,6 @@ pub struct InvoiceParams { pub struct OpenChannelFeeInvoiceParams { pub amount: u64, pub channel_funding_txid: String, - pub description: Option, pub expiry: Option, } @@ -231,12 +230,7 @@ pub async fn get_open_channel_fee_invoice( ) -> Result { let invoice = state .node - .channel_opening_fee_invoice( - params.amount, - params.channel_funding_txid, - params.description, - params.expiry, - ) + .channel_opening_fee_invoice(params.amount, params.channel_funding_txid, params.expiry) .await .map_err(|e| AppError::InternalServerError(format!("Failed to create invoice: {e:#}")))?; diff --git a/crates/orderbook-commons/src/lib.rs b/crates/orderbook-commons/src/lib.rs index 7c0038d1a..0f352d4fc 100644 --- a/crates/orderbook-commons/src/lib.rs +++ b/crates/orderbook-commons/src/lib.rs @@ -21,7 +21,11 @@ pub use crate::price::Price; pub use crate::price::Prices; /// The prefix used in the description field of an order-matching fee invoice to be paid by a taker. -pub const FEE_INVOICE_DESCRIPTION_PREFIX_TAKER: &str = "taker-fee-"; +pub const FEE_INVOICE_DESCRIPTION_PREFIX_TAKER: &str = "taker-fee"; + +/// The prefix used in the description field of an JIT channel opening invoice to be paid by the +/// client. +pub const JIT_FEE_INVOICE_DESCRIPTION_PREFIX: &str = "jit-channel-fee"; #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct Order { diff --git a/mobile/lib/features/wallet/domain/wallet_history.dart b/mobile/lib/features/wallet/domain/wallet_history.dart index 5416d33a4..6ba2b3abd 100644 --- a/mobile/lib/features/wallet/domain/wallet_history.dart +++ b/mobile/lib/features/wallet/domain/wallet_history.dart @@ -2,7 +2,7 @@ import 'package:get_10101/common/domain/model.dart'; import 'payment_flow.dart'; import 'package:get_10101/bridge_generated/bridge_definitions.dart' as rust; -enum WalletHistoryItemDataType { lightning, onChain, trade, orderMatchingFee } +enum WalletHistoryItemDataType { lightning, onChain, trade, orderMatchingFee, jitChannelFee } enum WalletHistoryStatus { pending, confirmed } @@ -13,10 +13,10 @@ class WalletHistoryItemData { final WalletHistoryStatus status; final DateTime timestamp; - // on-chain + // on-chain (payment txid) and jit fee (funding txid) final String? txid; - // lightning + // lightning and jit fee final String? paymentHash; // trade @@ -78,6 +78,20 @@ class WalletHistoryItemData { orderId: type.orderId); } + if (item.walletType is rust.WalletType_JitChannelFee) { + rust.WalletType_JitChannelFee type = item.walletType as rust.WalletType_JitChannelFee; + + return WalletHistoryItemData( + flow: flow, + amount: amount, + type: WalletHistoryItemDataType.jitChannelFee, + status: status, + timestamp: timestamp, + paymentHash: type.paymentHash, + txid: type.fundingTxid, + ); + } + return WalletHistoryItemData( flow: flow, amount: amount, diff --git a/mobile/lib/features/wallet/wallet_history_item.dart b/mobile/lib/features/wallet/wallet_history_item.dart index 8663853f4..af5119022 100644 --- a/mobile/lib/features/wallet/wallet_history_item.dart +++ b/mobile/lib/features/wallet/wallet_history_item.dart @@ -34,7 +34,8 @@ class WalletHistoryItem extends StatelessWidget { Icons.bar_chart, size: flowIconSize, ); - } else if (data.type == WalletHistoryItemDataType.orderMatchingFee) { + } else if (data.type == WalletHistoryItemDataType.orderMatchingFee || + data.type == WalletHistoryItemDataType.jitChannelFee) { return const Icon( Icons.toll, size: flowIconSize, @@ -66,6 +67,8 @@ class WalletHistoryItem extends StatelessWidget { } case WalletHistoryItemDataType.orderMatchingFee: return "Matching fee"; + case WalletHistoryItemDataType.jitChannelFee: + return "Channel opening fee"; } }(); @@ -74,6 +77,7 @@ class WalletHistoryItem extends StatelessWidget { case WalletHistoryItemDataType.lightning: case WalletHistoryItemDataType.trade: case WalletHistoryItemDataType.orderMatchingFee: + case WalletHistoryItemDataType.jitChannelFee: return "off-chain"; case WalletHistoryItemDataType.onChain: return "on-chain"; @@ -158,16 +162,21 @@ class WalletHistoryItem extends StatelessWidget { } Widget showItemDetails(String title, BuildContext context) { - var [label, id] = () { + List details = () { switch (data.type) { case WalletHistoryItemDataType.lightning: - return ["Payment hash", data.paymentHash ?? ""]; + return [HistoryDetail(label: "Payment hash", value: data.paymentHash ?? "")]; case WalletHistoryItemDataType.onChain: - return ["Transaction id", data.txid ?? ""]; + return [HistoryDetail(label: "Transaction id", value: data.txid ?? "")]; case WalletHistoryItemDataType.trade: case WalletHistoryItemDataType.orderMatchingFee: final orderId = data.orderId!.substring(0, 8); - return ["Order", orderId]; + return [HistoryDetail(label: "Order", value: orderId)]; + case WalletHistoryItemDataType.jitChannelFee: + return [ + HistoryDetail(label: "Payment hash", value: data.paymentHash ?? ""), + HistoryDetail(label: "Funding transaction id", value: data.txid ?? "") + ]; } }(); @@ -185,7 +194,7 @@ class WalletHistoryItem extends StatelessWidget { content: Column( mainAxisSize: MainAxisSize.min, children: [ - HistoryDetail(label: label, value: id), + ...details, HistoryDetail( label: "Amount", value: formatSats(Amount(data.amount.sats * directionMultiplier))), HistoryDetail(label: "Date and time", value: dateFormat.format(data.timestamp)), diff --git a/mobile/native/src/api.rs b/mobile/native/src/api.rs index 5d085b6e1..b00324d5d 100644 --- a/mobile/native/src/api.rs +++ b/mobile/native/src/api.rs @@ -71,10 +71,22 @@ pub struct WalletHistoryItem { #[derive(Clone, Debug)] pub enum WalletType { - OnChain { txid: String }, - Lightning { payment_hash: String }, - Trade { order_id: String }, - OrderMatchingFee { order_id: String }, + OnChain { + txid: String, + }, + Lightning { + payment_hash: String, + }, + Trade { + order_id: String, + }, + OrderMatchingFee { + order_id: String, + }, + JitChannelFee { + funding_txid: String, + payment_hash: String, + }, } #[derive(Clone, Debug, Default)] diff --git a/mobile/native/src/ln_dlc/mod.rs b/mobile/native/src/ln_dlc/mod.rs index 09f11220e..4a3de73b5 100644 --- a/mobile/native/src/ln_dlc/mod.rs +++ b/mobile/native/src/ln_dlc/mod.rs @@ -40,6 +40,7 @@ use ln_dlc_node::AppEventHandler; use ln_dlc_node::CONFIRMATION_TARGET; use orderbook_commons::RouteHintHop; use orderbook_commons::FEE_INVOICE_DESCRIPTION_PREFIX_TAKER; +use orderbook_commons::JIT_FEE_INVOICE_DESCRIPTION_PREFIX; use rust_decimal::Decimal; use state::Storage; use std::net::IpAddr; @@ -338,11 +339,19 @@ fn keep_wallet_balance_and_history_up_to_date(node: &Node) -> Result<()> { let payment_hash = hex::encode(details.payment_hash.0); let description = &details.description; - let wallet_type = match description.strip_prefix(FEE_INVOICE_DESCRIPTION_PREFIX_TAKER) { - Some(order_id) => api::WalletType::OrderMatchingFee { - order_id: order_id.to_string(), - }, - None => api::WalletType::Lightning { payment_hash }, + let wallet_type = match description.split_once(' ') { + Some((prefix, order_id)) if prefix == FEE_INVOICE_DESCRIPTION_PREFIX_TAKER => { + api::WalletType::OrderMatchingFee { + order_id: order_id.to_string(), + } + } + Some((prefix, funding_txid)) if prefix == JIT_FEE_INVOICE_DESCRIPTION_PREFIX => { + api::WalletType::JitChannelFee { + funding_txid: funding_txid.to_string(), + payment_hash, + } + } + _ => api::WalletType::Lightning { payment_hash }, }; Some(api::WalletHistoryItem {