Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 41 additions & 4 deletions lib/frb_generated.dart
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@ abstract class RustLibApi extends BaseApi {
required SafeUrl gatewayUrl,
required bool isLnv2,
required Amount amount,
required bool includeFees,
});

Future<bool> crateMultimintMultimintContainsClient({
Expand Down Expand Up @@ -921,6 +922,7 @@ abstract class RustLibApi extends BaseApi {
required String gatewayUrl,
required bool isLnv2,
required BigInt amountMsats,
required bool includeFees,
});

Future<Connector> crateDbConnectorDefault();
Expand Down Expand Up @@ -3551,6 +3553,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
required SafeUrl gatewayUrl,
required bool isLnv2,
required Amount amount,
required bool includeFees,
}) {
return handler.executeNormal(
NormalTask(
Expand All @@ -3573,6 +3576,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
amount,
serializer,
);
sse_encode_bool(includeFees, serializer);
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
Expand All @@ -3586,7 +3590,14 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
),
constMeta:
kCrateMultimintMultimintComputeReceiveAmountWithFeesConstMeta,
argValues: [that, federationId, gatewayUrl, isLnv2, amount],
argValues: [
that,
federationId,
gatewayUrl,
isLnv2,
amount,
includeFees,
],
apiImpl: this,
),
);
Expand All @@ -3596,7 +3607,14 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
get kCrateMultimintMultimintComputeReceiveAmountWithFeesConstMeta =>
const TaskConstMeta(
debugName: "Multimint_compute_receive_amount_with_fees",
argNames: ["that", "federationId", "gatewayUrl", "isLnv2", "amount"],
argNames: [
"that",
"federationId",
"gatewayUrl",
"isLnv2",
"amount",
"includeFees",
],
);

@override
Expand Down Expand Up @@ -8158,6 +8176,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
required String gatewayUrl,
required bool isLnv2,
required BigInt amountMsats,
required bool includeFees,
}) {
return handler.executeNormal(
NormalTask(
Expand All @@ -8170,6 +8189,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
sse_encode_String(gatewayUrl, serializer);
sse_encode_bool(isLnv2, serializer);
sse_encode_u_64(amountMsats, serializer);
sse_encode_bool(includeFees, serializer);
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
Expand All @@ -8182,7 +8202,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
decodeErrorData: sse_decode_AnyhowException,
),
constMeta: kCrateComputeReceiveAmountWithFeesConstMeta,
argValues: [federationId, gatewayUrl, isLnv2, amountMsats],
argValues: [federationId, gatewayUrl, isLnv2, amountMsats, includeFees],
apiImpl: this,
),
);
Expand All @@ -8191,7 +8211,13 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
TaskConstMeta get kCrateComputeReceiveAmountWithFeesConstMeta =>
const TaskConstMeta(
debugName: "compute_receive_amount_with_fees",
argNames: ["federationId", "gatewayUrl", "isLnv2", "amountMsats"],
argNames: [
"federationId",
"gatewayUrl",
"isLnv2",
"amountMsats",
"includeFees",
],
);

@override
Expand Down Expand Up @@ -19723,18 +19749,29 @@ class MultimintImpl extends RustOpaque implements Multimint {
bolt11: bolt11,
);

/// Computes the invoice amount and fee breakdown for a receive.
///
/// When `include_fees` is true the fees are added on top of `amount`, so the
/// sender covers them and exactly `amount` is credited to the receiver. When
/// it is false the invoice equals `amount` exactly — the sender pays only what
/// was requested and the receiver absorbs the fees, netting `amount` minus the
/// fees. In both cases the returned `invoice_msats` is the invoice face value
/// and the receiver nets `invoice_msats - federation_fee_msats -
/// gateway_fee_msats`.
Future<ReceiveAmount> computeReceiveAmountWithFees({
required FederationId federationId,
required SafeUrl gatewayUrl,
required bool isLnv2,
required Amount amount,
required bool includeFees,
}) =>
RustLib.instance.api.crateMultimintMultimintComputeReceiveAmountWithFees(
that: this,
federationId: federationId,
gatewayUrl: gatewayUrl,
isLnv2: isLnv2,
amount: amount,
includeFees: includeFees,
);

Future<bool> containsClient({required FederationId federationId}) =>
Expand Down
6 changes: 6 additions & 0 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,12 @@
"showMillisatoshisDescription": "Display fractional satoshis with 3 decimal places",
"@showMillisatoshisDescription": {},

"addFeesToInvoice": "Add fees to invoice",
"@addFeesToInvoice": {},

"addFeesToInvoiceDescription": "When on, the sender pays the fees and you receive the full amount. When off, the invoice is for the exact amount and the fees are taken from what you receive.",
"@addFeesToInvoiceDescription": {},

"txDetailAmount": "Amount",
"@txDetailAmount": {},

Expand Down
2 changes: 2 additions & 0 deletions lib/l10n/app_es.arb
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@
"displaySettingsUpdated": "¡Configuración de pantalla actualizada!",
"showMillisatoshis": "Mostrar milisatoshis",
"showMillisatoshisDescription": "Mostrar satoshis fraccionarios con 3 decimales",
"addFeesToInvoice": "Agregar comisiones a la factura",
"addFeesToInvoiceDescription": "Cuando está activado, el remitente paga las comisiones y tú recibes el monto completo. Cuando está desactivado, la factura es por el monto exacto y las comisiones se descuentan de lo que recibes.",
"txDetailAmount": "Monto",
"txDetailTotalAmount": "Monto Total",
"txDetailReceivedAmount": "Monto Recibido",
Expand Down
2 changes: 2 additions & 0 deletions lib/lib.dart
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,13 @@ Future<ReceiveAmount> computeReceiveAmountWithFees({
required String gatewayUrl,
required bool isLnv2,
required BigInt amountMsats,
required bool includeFees,
}) => RustLib.instance.api.crateComputeReceiveAmountWithFees(
federationId: federationId,
gatewayUrl: gatewayUrl,
isLnv2: isLnv2,
amountMsats: amountMsats,
includeFees: includeFees,
);

Future<String> getInvoiceFromLnaddressOrLnurl({
Expand Down
21 changes: 16 additions & 5 deletions lib/multimint.dart
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,21 @@ abstract class Multimint implements RustOpaqueInterface {
required Bolt11Invoice bolt11,
});

/// Computes the invoice amount and fee breakdown for a receive.
///
/// When `include_fees` is true the fees are added on top of `amount`, so the
/// sender covers them and exactly `amount` is credited to the receiver. When
/// it is false the invoice equals `amount` exactly — the sender pays only what
/// was requested and the receiver absorbs the fees, netting `amount` minus the
/// fees. In both cases the returned `invoice_msats` is the invoice face value
/// and the receiver nets `invoice_msats - federation_fee_msats -
/// gateway_fee_msats`.
Future<ReceiveAmount> computeReceiveAmountWithFees({
required FederationId federationId,
required SafeUrl gatewayUrl,
required bool isLnv2,
required Amount amount,
required bool includeFees,
});

Future<bool> containsClient({required FederationId federationId});
Expand Down Expand Up @@ -852,11 +862,12 @@ class PeerStatus {

/// Result of pricing a Lightning receive. `invoice_msats` is the invoice's face
/// value (what the payer pays). The fee is broken out into its two sources:
/// `federation_fee_msats` (on-federation: lightning input fee + mint output fees
/// + dust) and `gateway_fee_msats` (the gateway's off-chain routing fee, always
/// 0 for LNv1). The receiver is credited `invoice_msats - federation_fee_msats -
/// gateway_fee_msats`. Both are quoted at `invoice_msats`, not the requested
/// amount, so they match what is really deducted.
/// `federation_fee_msats` (on-federation: lightning input fee plus mint output
/// fees plus dust) and `gateway_fee_msats` (the gateway's off-chain routing fee,
/// always 0 for LNv1). The receiver is credited
/// `invoice_msats - federation_fee_msats - gateway_fee_msats`. Both are quoted at
/// `invoice_msats`, not the requested amount, so they match what is really
/// deducted.
class ReceiveAmount {
final BigInt invoiceMsats;
final BigInt federationFeeMsats;
Expand Down
47 changes: 47 additions & 0 deletions lib/number_pad.dart
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,18 @@ class _NumberPadState extends State<NumberPad> {
String? _selectedGatewayEndpoint;
bool? _selectedGatewayIsLnv2;

// When true, fees are added on top of the invoice so the sender pays them and
// the receiver gets the full requested amount. When false, the invoice is for
// the exact requested amount and the receiver absorbs the fees. Resets to true
// each time the screen opens. Only relevant for Lightning receive.
bool _includeFees = true;

// The toggle only makes sense when generating a Lightning invoice to receive,
// not when paying a Lightning Address / LNURL or for ecash / on-chain.
bool get _isLightningReceive =>
widget.paymentType == PaymentType.lightning &&
widget.lightningAddressOrLnurl == null;

@override
void initState() {
super.initState();
Expand Down Expand Up @@ -330,6 +342,7 @@ class _NumberPadState extends State<NumberPad> {
gatewayUrl: selected.endpoint,
isLnv2: selected.isLnv2,
amountMsats: requestedAmountMsats,
includeFees: _includeFees,
);

// Create the invoice
Expand Down Expand Up @@ -990,6 +1003,40 @@ class _NumberPadState extends State<NumberPad> {
),
),
),
if (_isLightningReceive)
Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 8, 8),
child: Row(
children: [
Expanded(
child: Text(
context.l10n.addFeesToInvoice,
style: Theme.of(context).textTheme.bodyMedium,
),
),
Tooltip(
message: context.l10n.addFeesToInvoiceDescription,
triggerMode: TooltipTriggerMode.tap,
showDuration: const Duration(seconds: 4),
child: Padding(
padding: const EdgeInsets.only(right: 4),
child: Icon(
Icons.info_outline,
size: 18,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
),
Switch(
value: _includeFees,
activeThumbColor: Theme.of(context).colorScheme.primary,
onChanged: (value) {
setState(() => _includeFees = value);
},
),
],
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: SizedBox(
Expand Down
4 changes: 4 additions & 0 deletions rust/ecashapp/src/frb_generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3171,6 +3171,7 @@ fn wire__crate__multimint__Multimint_compute_receive_amount_with_fees_impl(
let api_gateway_url = <SafeUrl>::sse_decode(&mut deserializer);
let api_is_lnv2 = <bool>::sse_decode(&mut deserializer);
let api_amount = <Amount>::sse_decode(&mut deserializer);
let api_include_fees = <bool>::sse_decode(&mut deserializer);
deserializer.end();
move |context| async move {
transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>(
Expand Down Expand Up @@ -3212,6 +3213,7 @@ fn wire__crate__multimint__Multimint_compute_receive_amount_with_fees_impl(
api_gateway_url,
api_is_lnv2,
api_amount,
api_include_fees,
)
.await?;
Ok(output_ok)
Expand Down Expand Up @@ -10017,6 +10019,7 @@ fn wire__crate__compute_receive_amount_with_fees_impl(
let api_gateway_url = <String>::sse_decode(&mut deserializer);
let api_is_lnv2 = <bool>::sse_decode(&mut deserializer);
let api_amount_msats = <u64>::sse_decode(&mut deserializer);
let api_include_fees = <bool>::sse_decode(&mut deserializer);
deserializer.end();
move |context| async move {
transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>(
Expand Down Expand Up @@ -10045,6 +10048,7 @@ fn wire__crate__compute_receive_amount_with_fees_impl(
api_gateway_url,
api_is_lnv2,
api_amount_msats,
api_include_fees,
)
.await?;
Ok(output_ok)
Expand Down
3 changes: 2 additions & 1 deletion rust/ecashapp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,12 +282,13 @@ pub async fn compute_receive_amount_with_fees(
gateway_url: String,
is_lnv2: bool,
amount_msats: u64,
include_fees: bool,
) -> anyhow::Result<ReceiveAmount> {
let gateway_url = SafeUrl::parse(&gateway_url)?;
let amount = Amount::from_msats(amount_msats);
let multimint = get_multimint();
multimint
.compute_receive_amount_with_fees(federation_id, gateway_url, is_lnv2, amount)
.compute_receive_amount_with_fees(federation_id, gateway_url, is_lnv2, amount, include_fees)
.await
}

Expand Down
Loading
Loading