Skip to content

Commit

Permalink
Merge pull request #615 from breez/updated-swap-fees-user-review
Browse files Browse the repository at this point in the history
Expose updated swap fees for user review
  • Loading branch information
danielgranhao authored Jan 5, 2025
2 parents ba6c411 + fa70c0b commit 6d06580
Show file tree
Hide file tree
Showing 43 changed files with 1,578 additions and 118 deletions.
21 changes: 21 additions & 0 deletions cli/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ pub(crate) enum Command {
/// Lightning payment hash
payment_hash: String,
},
/// Get and potentially accept proposed fees for WaitingFeeAcceptance Payment
ReviewPaymentProposedFees { swap_id: String },
/// List refundable chain swaps
ListRefundables,
/// Prepare a refund transaction for an incomplete swap
Expand Down Expand Up @@ -519,6 +521,25 @@ pub(crate) async fn handle_command(
}
}
}
Command::ReviewPaymentProposedFees { swap_id } => {
let fetch_response = sdk
.fetch_payment_proposed_fees(&FetchPaymentProposedFeesRequest { swap_id })
.await?;

let confirmation_msg = format!(
"Payer amount: {} sat. Fees: {} sat. Resulting received amount: {} sat. Are the fees acceptable? (y/N) ",
fetch_response.payer_amount_sat, fetch_response.fees_sat, fetch_response.receiver_amount_sat
);

wait_confirmation!(confirmation_msg, "Payment proposed fees review halted");

sdk.accept_payment_proposed_fees(&AcceptPaymentProposedFeesRequest {
response: fetch_response,
})
.await?;

command_result!("Proposed fees accepted successfully")
}
Command::ListRefundables => {
let refundables = sdk.list_refundables().await?;
command_result!(refundables)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ object Constants {
"payment_sent_notification_text"
const val PAYMENT_SENT_NOTIFICATION_TITLE =
"payment_sent_notification_title"
const val PAYMENT_WAITING_FEE_ACCEPTANCE_TITLE =
"payment_waiting_fee_acceptance_notification_title"
const val PAYMENT_WAITING_FEE_ACCEPTANCE_TEXT =
"payment_waiting_fee_acceptance_text"
const val SWAP_CONFIRMED_NOTIFICATION_FAILURE_TEXT =
"swap_confirmed_notification_failure_text"
const val SWAP_CONFIRMED_NOTIFICATION_FAILURE_TITLE =
Expand Down Expand Up @@ -102,6 +106,10 @@ object Constants {
"Sent %d sats"
const val DEFAULT_PAYMENT_SENT_NOTIFICATION_TITLE =
"Payment Sent"
const val DEFAULT_PAYMENT_WAITING_FEE_ACCEPTANCE_TITLE =
"Payment requires fee acceptance"
const val DEFAULT_PAYMENT_WAITING_FEE_ACCEPTANCE_TEXT =
"Tap to review updated fees"
const val DEFAULT_SWAP_CONFIRMED_NOTIFICATION_FAILURE_TEXT =
"Tap to complete payment"
const val DEFAULT_SWAP_CONFIRMED_NOTIFICATION_FAILURE_TITLE =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@ import breez_sdk_liquid_notification.Constants.DEFAULT_PAYMENT_RECEIVED_NOTIFICA
import breez_sdk_liquid_notification.Constants.DEFAULT_PAYMENT_RECEIVED_NOTIFICATION_TITLE
import breez_sdk_liquid_notification.Constants.DEFAULT_PAYMENT_SENT_NOTIFICATION_TEXT
import breez_sdk_liquid_notification.Constants.DEFAULT_PAYMENT_SENT_NOTIFICATION_TITLE
import breez_sdk_liquid_notification.Constants.DEFAULT_PAYMENT_WAITING_FEE_ACCEPTANCE_TEXT
import breez_sdk_liquid_notification.Constants.DEFAULT_PAYMENT_WAITING_FEE_ACCEPTANCE_TITLE
import breez_sdk_liquid_notification.Constants.DEFAULT_SWAP_CONFIRMED_NOTIFICATION_FAILURE_TEXT
import breez_sdk_liquid_notification.Constants.DEFAULT_SWAP_CONFIRMED_NOTIFICATION_FAILURE_TITLE
import breez_sdk_liquid_notification.Constants.NOTIFICATION_CHANNEL_SWAP_UPDATED
import breez_sdk_liquid_notification.Constants.PAYMENT_RECEIVED_NOTIFICATION_TEXT
import breez_sdk_liquid_notification.Constants.PAYMENT_RECEIVED_NOTIFICATION_TITLE
import breez_sdk_liquid_notification.Constants.PAYMENT_SENT_NOTIFICATION_TEXT
import breez_sdk_liquid_notification.Constants.PAYMENT_SENT_NOTIFICATION_TITLE
import breez_sdk_liquid_notification.Constants.PAYMENT_WAITING_FEE_ACCEPTANCE_TEXT
import breez_sdk_liquid_notification.Constants.PAYMENT_WAITING_FEE_ACCEPTANCE_TITLE
import breez_sdk_liquid_notification.Constants.SWAP_CONFIRMED_NOTIFICATION_FAILURE_TEXT
import breez_sdk_liquid_notification.Constants.SWAP_CONFIRMED_NOTIFICATION_FAILURE_TITLE
import breez_sdk_liquid_notification.NotificationHelper.Companion.notifyChannel
Expand Down Expand Up @@ -57,8 +61,9 @@ class SwapUpdatedJob(

override fun onEvent(e: SdkEvent) {
when (e) {
is SdkEvent.PaymentWaitingConfirmation -> handlePaymentEvent(e.details)
is SdkEvent.PaymentSucceeded -> handlePaymentEvent(e.details)
is SdkEvent.PaymentWaitingConfirmation -> handlePaymentSuccess(e.details)
is SdkEvent.PaymentSucceeded -> handlePaymentSuccess(e.details)
is SdkEvent.PaymentWaitingFeeAcceptance -> handlePaymentWaitingFeeAcceptance(e.details)

else -> {
logger.log(TAG, "Received event: ${e}", "TRACE")
Expand All @@ -76,12 +81,16 @@ class SwapUpdatedJob(
.fold(StringBuilder()) { sb, it -> sb.append("%02x".format(it)) }
.toString()

private fun handlePaymentEvent(payment: Payment) {
val swapId = when (val details = payment.details) {
private fun getSwapId(details: PaymentDetails?): String? {
return when (details) {
is PaymentDetails.Bitcoin -> details.swapId
is PaymentDetails.Lightning -> details.swapId
else -> null
}
}

private fun handlePaymentSuccess(payment: Payment) {
val swapId = getSwapId(payment.details)

swapId?.let {
if (this.swapIdHash == hashId(it)) {
Expand All @@ -95,6 +104,21 @@ class SwapUpdatedJob(
}
}

private fun handlePaymentWaitingFeeAcceptance(payment: Payment) {
val swapId = getSwapId(payment.details)

swapId?.let {
if (this.swapIdHash == hashId(it)) {
logger.log(
TAG,
"Payment waiting fee acceptance: ${this.swapIdHash}",
"TRACE"
)
notifyPaymentWaitingFeeAcceptance(payment)
}
}
}

private fun notifySuccess(payment: Payment) {
if (!this.notified) {
logger.log(TAG, "Payment ${payment.txId} processing successful", "INFO")
Expand All @@ -121,6 +145,28 @@ class SwapUpdatedJob(
}
}

private fun notifyPaymentWaitingFeeAcceptance(payment: Payment) {
if (!this.notified) {
logger.log(TAG, "Payment with swap ID ${getSwapId(payment.details)} requires fee acceptance", "INFO")
notifyChannel(
context,
NOTIFICATION_CHANNEL_SWAP_UPDATED,
getString(
context,
PAYMENT_WAITING_FEE_ACCEPTANCE_TITLE,
DEFAULT_PAYMENT_WAITING_FEE_ACCEPTANCE_TITLE
),
getString(
context,
PAYMENT_WAITING_FEE_ACCEPTANCE_TEXT,
DEFAULT_PAYMENT_WAITING_FEE_ACCEPTANCE_TEXT
)
)
this.notified = true
fgService.onFinished(this)
}
}

private fun notifyFailure() {
this.swapIdHash?.let { swapIdHash ->
logger.log(TAG, "Swap $swapIdHash processing failed", "INFO")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ typedef struct _Dart_Handle* Dart_Handle;

#define ESTIMATED_BTC_CLAIM_TX_VSIZE 111

#define ESTIMATED_BTC_LOCKUP_TX_VSIZE 154

#define STANDARD_FEE_RATE_SAT_PER_VBYTE 0.1

#define LOWBALL_FEE_RATE_SAT_PER_VBYTE 0.01
Expand Down Expand Up @@ -541,13 +543,18 @@ typedef struct wire_cst_SdkEvent_PaymentWaitingConfirmation {
struct wire_cst_payment *details;
} wire_cst_SdkEvent_PaymentWaitingConfirmation;

typedef struct wire_cst_SdkEvent_PaymentWaitingFeeAcceptance {
struct wire_cst_payment *details;
} wire_cst_SdkEvent_PaymentWaitingFeeAcceptance;

typedef union SdkEventKind {
struct wire_cst_SdkEvent_PaymentFailed PaymentFailed;
struct wire_cst_SdkEvent_PaymentPending PaymentPending;
struct wire_cst_SdkEvent_PaymentRefunded PaymentRefunded;
struct wire_cst_SdkEvent_PaymentRefundPending PaymentRefundPending;
struct wire_cst_SdkEvent_PaymentSucceeded PaymentSucceeded;
struct wire_cst_SdkEvent_PaymentWaitingConfirmation PaymentWaitingConfirmation;
struct wire_cst_SdkEvent_PaymentWaitingFeeAcceptance PaymentWaitingFeeAcceptance;
} SdkEventKind;

typedef struct wire_cst_sdk_event {
Expand Down Expand Up @@ -580,6 +587,7 @@ typedef struct wire_cst_config {
struct wire_cst_list_prim_u_8_strict *breez_api_key;
struct wire_cst_list_external_input_parser *external_input_parsers;
bool use_default_external_input_parsers;
uint32_t *onchain_fee_rate_leeway_sat_per_vbyte;
} wire_cst_config;

typedef struct wire_cst_connect_request {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ typedef void (*UniFfiRustFutureContinuation)(void * _Nonnull, int8_t);
// Scaffolding functions
void uniffi_breez_sdk_liquid_bindings_fn_free_bindingliquidsdk(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status
);
void uniffi_breez_sdk_liquid_bindings_fn_method_bindingliquidsdk_accept_payment_proposed_fees(void*_Nonnull ptr, RustBuffer req, RustCallStatus *_Nonnull out_status
);
RustBuffer uniffi_breez_sdk_liquid_bindings_fn_method_bindingliquidsdk_add_event_listener(void*_Nonnull ptr, uint64_t listener, RustCallStatus *_Nonnull out_status
);
void uniffi_breez_sdk_liquid_bindings_fn_method_bindingliquidsdk_backup(void*_Nonnull ptr, RustBuffer req, RustCallStatus *_Nonnull out_status
Expand All @@ -81,6 +83,8 @@ RustBuffer uniffi_breez_sdk_liquid_bindings_fn_method_bindingliquidsdk_fetch_lig
);
RustBuffer uniffi_breez_sdk_liquid_bindings_fn_method_bindingliquidsdk_fetch_onchain_limits(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status
);
RustBuffer uniffi_breez_sdk_liquid_bindings_fn_method_bindingliquidsdk_fetch_payment_proposed_fees(void*_Nonnull ptr, RustBuffer req, RustCallStatus *_Nonnull out_status
);
RustBuffer uniffi_breez_sdk_liquid_bindings_fn_method_bindingliquidsdk_get_info(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status
);
RustBuffer uniffi_breez_sdk_liquid_bindings_fn_method_bindingliquidsdk_get_payment(void*_Nonnull ptr, RustBuffer req, RustCallStatus *_Nonnull out_status
Expand Down Expand Up @@ -279,6 +283,9 @@ uint16_t uniffi_breez_sdk_liquid_bindings_checksum_func_parse_invoice(void
);
uint16_t uniffi_breez_sdk_liquid_bindings_checksum_func_set_logger(void

);
uint16_t uniffi_breez_sdk_liquid_bindings_checksum_method_bindingliquidsdk_accept_payment_proposed_fees(void

);
uint16_t uniffi_breez_sdk_liquid_bindings_checksum_method_bindingliquidsdk_add_event_listener(void

Expand All @@ -303,6 +310,9 @@ uint16_t uniffi_breez_sdk_liquid_bindings_checksum_method_bindingliquidsdk_fetch
);
uint16_t uniffi_breez_sdk_liquid_bindings_checksum_method_bindingliquidsdk_fetch_onchain_limits(void

);
uint16_t uniffi_breez_sdk_liquid_bindings_checksum_method_bindingliquidsdk_fetch_payment_proposed_fees(void

);
uint16_t uniffi_breez_sdk_liquid_bindings_checksum_method_bindingliquidsdk_get_info(void

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ struct Constants {
static let LNURL_PAY_NOTIFICATION_FAILURE_TITLE = "lnurl_pay_notification_failure_title"
static let PAYMENT_RECEIVED_NOTIFICATION_TITLE = "payment_received_notification_title"
static let PAYMENT_SENT_NOTIFICATION_TITLE = "payment_sent_notification_title"
static let PAYMENT_WAITING_FEE_ACCEPTANCE_TITLE = "payment_waiting_fee_acceptance_notification_title"
static let PAYMENT_WAITING_FEE_ACCEPTANCE_TEXT = "payment_waiting_fee_acceptance_text"
static let SWAP_CONFIRMED_NOTIFICATION_FAILURE_TEXT = "swap_confirmed_notification_failure_text"
static let SWAP_CONFIRMED_NOTIFICATION_FAILURE_TITLE = "swap_confirmed_notification_failure_title"

Expand All @@ -30,6 +32,8 @@ struct Constants {
static let DEFAULT_LNURL_PAY_NOTIFICATION_FAILURE_TITLE = "Receive Payment Failed"
static let DEFAULT_PAYMENT_RECEIVED_NOTIFICATION_TITLE = "Received %d sats"
static let DEFAULT_PAYMENT_SENT_NOTIFICATION_TITLE = "Sent %d sats"
static let DEFAULT_PAYMENT_WAITING_FEE_ACCEPTANCE_TITLE = "Payment requires fee acceptance"
static let DEFAULT_PAYMENT_WAITING_FEE_ACCEPTANCE_TEXT = "Tap to review updated fees"
static let DEFAULT_SWAP_CONFIRMED_NOTIFICATION_FAILURE_TEXT = "Tap to complete payment"
static let DEFAULT_SWAP_CONFIRMED_NOTIFICATION_FAILURE_TITLE = "Payment Pending"
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ class SwapUpdatedTask : TaskProtocol {
self.notifySuccess(payment: payment)
}
break
case .paymentWaitingFeeAcceptance(details: let payment):
let swapId = self.getSwapId(details: payment.details)
if swapIdHash == swapId?.sha256() {
self.logger.log(tag: TAG, line: "Received payment event: \(swapIdHash) \(payment.status)", level: "INFO")
self.notifyPaymentWaitingFeeAcceptance(payment: payment)
}
break
default:
break
}
Expand Down Expand Up @@ -81,4 +88,18 @@ class SwapUpdatedTask : TaskProtocol {
self.displayPushNotification(title: String(format: notificationTitle, payment.amountSat), logger: self.logger, threadIdentifier: Constants.NOTIFICATION_THREAD_SWAP_UPDATED)
}
}

func notifyPaymentWaitingFeeAcceptance(payment: Payment) {
if !self.notified {
self.logger.log(tag: TAG, line: "Payment \(self.getSwapId(details: payment.details) ?? "") requires fee acceptance", level: "INFO")
let notificationTitle = ResourceHelper.shared.getString(
key: Constants.PAYMENT_WAITING_FEE_ACCEPTANCE_TITLE,
fallback: Constants.DEFAULT_PAYMENT_WAITING_FEE_ACCEPTANCE_TITLE)
let notificationBody = ResourceHelper.shared.getString(
key: Constants.PAYMENT_WAITING_FEE_ACCEPTANCE_TEXT,
fallback: Constants.DEFAULT_PAYMENT_WAITING_FEE_ACCEPTANCE_TEXT)
self.notified = true
self.displayPushNotification(title: notificationTitle, body: notificationBody, logger: self.logger, threadIdentifier: Constants.NOTIFICATION_THREAD_SWAP_UPDATED)
}
}
}
24 changes: 24 additions & 0 deletions lib/bindings/src/breez_sdk_liquid.udl
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,7 @@ dictionary Config {
u64? zero_conf_max_amount_sat;
boolean use_default_external_input_parsers = true;
sequence<ExternalInputParser>? external_input_parsers = null;
u32? onchain_fee_rate_leeway_sat_per_vbyte = null;
};

enum LiquidNetwork {
Expand Down Expand Up @@ -541,6 +542,21 @@ interface GetPaymentRequest {
Lightning(string payment_hash);
};

dictionary FetchPaymentProposedFeesRequest {
string swap_id;
};

dictionary FetchPaymentProposedFeesResponse {
string swap_id;
u64 fees_sat;
u64 payer_amount_sat;
u64 receiver_amount_sat;
};

dictionary AcceptPaymentProposedFeesRequest {
FetchPaymentProposedFeesResponse response;
};

dictionary LnUrlInfo {
string? ln_address;
string? lnurl_pay_comment;
Expand Down Expand Up @@ -584,6 +600,7 @@ enum PaymentState {
"TimedOut",
"Refundable",
"RefundPending",
"WaitingFeeAcceptance",
};

dictionary RefundableSwap {
Expand Down Expand Up @@ -630,6 +647,7 @@ interface SdkEvent {
PaymentRefundPending(Payment details);
PaymentSucceeded(Payment details);
PaymentWaitingConfirmation(Payment details);
PaymentWaitingFeeAcceptance(Payment details);
Synced();
};

Expand Down Expand Up @@ -755,6 +773,12 @@ interface BindingLiquidSdk {
[Throws=PaymentError]
Payment? get_payment(GetPaymentRequest req);

[Throws=SdkError]
FetchPaymentProposedFeesResponse fetch_payment_proposed_fees(FetchPaymentProposedFeesRequest req);

[Throws=PaymentError]
void accept_payment_proposed_fees(AcceptPaymentProposedFeesRequest req);

[Throws=SdkError]
sequence<RefundableSwap> list_refundables();

Expand Down
14 changes: 14 additions & 0 deletions lib/bindings/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,20 @@ impl BindingLiquidSdk {
rt().block_on(self.sdk.get_payment(&req))
}

pub fn fetch_payment_proposed_fees(
&self,
req: FetchPaymentProposedFeesRequest,
) -> SdkResult<FetchPaymentProposedFeesResponse> {
rt().block_on(self.sdk.fetch_payment_proposed_fees(&req))
}

pub fn accept_payment_proposed_fees(
&self,
req: AcceptPaymentProposedFeesRequest,
) -> Result<(), PaymentError> {
rt().block_on(self.sdk.accept_payment_proposed_fees(&req))
}

pub fn prepare_lnurl_pay(
&self,
req: PrepareLnUrlPayRequest,
Expand Down
Loading

0 comments on commit 6d06580

Please sign in to comment.