Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Pay lightning invoice with USDP #1706

Merged
merged 4 commits into from
Dec 8, 2023
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
2 changes: 0 additions & 2 deletions coordinator/src/db/positions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@ impl Position {
liquidation_price: f32,
expiry_timestamp: OffsetDateTime,
temporary_contract_id: ContractId,
stable: bool,
) -> Result<()> {
let affected_rows = diesel::update(positions::table)
.filter(positions::trader_pubkey.eq(trader_pubkey.clone()))
Expand All @@ -194,7 +193,6 @@ impl Position {
positions::liquidation_price.eq(liquidation_price),
positions::expiry_timestamp.eq(expiry_timestamp),
positions::temporary_contract_id.eq(temporary_contract_id.to_hex()),
positions::stable.eq(stable),
))
.execute(conn)?;

Expand Down
5 changes: 0 additions & 5 deletions coordinator/src/node/resize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,10 +174,6 @@ impl Node {
.dlc_expiry_timestamp
.context("No expiry timestamp for resizing trade")?;

let stable = old_position.stable
&& leverage_trader == Decimal::ONE
&& direction == Direction::Short;

let total_contracts = f32_from_decimal(total_contracts);
let leverage_coordinator = f32_from_decimal(leverage_coordinator);
let leverage_trader = f32_from_decimal(leverage_trader);
Expand Down Expand Up @@ -283,7 +279,6 @@ impl Node {
f32_from_decimal(liquidation_price_trader),
expiry_timestamp,
temporary_contract_id,
stable,
) {
tracing::error!(
channel_id = %channel_details.channel_id.to_hex(),
Expand Down
9 changes: 7 additions & 2 deletions mobile/lib/common/application/switch.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import 'package:get_10101/common/color.dart';
class TenTenOneSwitch extends StatefulWidget {
final bool value;
final ValueChanged<bool> onChanged;
final bool showDisabled;

const TenTenOneSwitch({Key? key, required this.value, required this.onChanged}) : super(key: key);
const TenTenOneSwitch(
{Key? key, required this.value, required this.onChanged, this.showDisabled = true})
: super(key: key);

@override
State<TenTenOneSwitch> createState() => _TenTenOneSwitchState();
Expand Down Expand Up @@ -47,7 +50,9 @@ class _TenTenOneSwitchState extends State<TenTenOneSwitch> with SingleTickerProv
borderRadius: BorderRadius.circular(24.0),
color: _circleAnimation!.value == Alignment.centerLeft
? tenTenOnePurple.shade300
: tenTenOnePurple.shade100),
: widget.showDisabled
? tenTenOnePurple.shade100
: tenTenOnePurple.shade300),
child: Padding(
padding: const EdgeInsets.only(top: 6.0, bottom: 6.0, left: 5.0, right: 5.0),
child: Container(
Expand Down
15 changes: 12 additions & 3 deletions mobile/lib/common/routes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import 'package:get_10101/common/global_keys.dart';
import 'package:get_10101/common/settings/channel_screen.dart';
import 'package:get_10101/common/status_screen.dart';
import 'package:get_10101/features/wallet/domain/destination.dart';
import 'package:get_10101/features/wallet/send/send_lightning_screen.dart';
import 'package:get_10101/features/wallet/send/send_onchain_screen.dart';
import 'package:get_10101/features/welcome/loading_screen.dart';
import 'package:get_10101/common/scaffold_with_nav_bar.dart';
import 'package:get_10101/common/settings/app_info_screen.dart';
Expand All @@ -19,7 +21,6 @@ import 'package:get_10101/features/wallet/receive_screen.dart';
import 'package:get_10101/features/wallet/scanner_screen.dart';
import 'package:get_10101/features/welcome/seed_import_screen.dart';
import 'package:get_10101/common/settings/seed_screen.dart';
import 'package:get_10101/features/wallet/send/send_screen.dart';
import 'package:get_10101/features/wallet/wallet_screen.dart';
import 'package:get_10101/features/welcome/welcome_screen.dart';
import 'package:go_router/go_router.dart';
Expand Down Expand Up @@ -149,11 +150,19 @@ GoRouter createRoutes() {
},
routes: <RouteBase>[
GoRoute(
path: SendScreen.subRouteName,
path: SendOnChainScreen.subRouteName,
// Use root navigator so the screen overlays the application shell
parentNavigatorKey: rootNavigatorKey,
builder: (BuildContext context, GoRouterState state) {
return SendScreen(destination: state.extra as Destination);
return SendOnChainScreen(destination: state.extra as OnChainAddress);
},
),
GoRoute(
path: SendLightningScreen.subRouteName,
// Use root navigator so the screen overlays the application shell
parentNavigatorKey: rootNavigatorKey,
builder: (BuildContext context, GoRouterState state) {
return SendLightningScreen(destination: state.extra as LightningInvoice);
},
),
GoRoute(
Expand Down
30 changes: 26 additions & 4 deletions mobile/lib/features/wallet/scanner_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import 'package:flutter/services.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:get_10101/common/color.dart';
import 'package:get_10101/features/wallet/domain/destination.dart';
import 'package:get_10101/features/wallet/send/send_screen.dart';
import 'package:get_10101/features/wallet/domain/wallet_type.dart';
import 'package:get_10101/features/wallet/send/send_lightning_screen.dart';
import 'package:get_10101/features/wallet/send/send_onchain_screen.dart';
import 'package:get_10101/features/wallet/wallet_change_notifier.dart';
import 'package:get_10101/features/wallet/wallet_screen.dart';
import 'package:get_10101/logger/logger.dart';
Expand Down Expand Up @@ -89,7 +91,7 @@ class _ScannerScreenState extends State<ScannerScreen> {
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(FontAwesomeIcons.ban, size: 55),
Icon(FontAwesomeIcons.eyeSlash, size: 55),
SizedBox(height: 20),
Text(
"To scan a QR code you must give 10101 permission to access the camera.",
Expand All @@ -113,7 +115,17 @@ class _ScannerScreenState extends State<ScannerScreen> {
.then((destination) {
_destination = destination;
if (_formKey.currentState!.validate()) {
GoRouter.of(context).go(SendScreen.route, extra: destination);
switch (destination!.getWalletType()) {
case WalletType.lightning:
GoRouter.of(context)
.go(SendLightningScreen.route, extra: destination);
case WalletType.onChain:
GoRouter.of(context)
.go(SendOnChainScreen.route, extra: destination);
case WalletType.stable:
GoRouter.of(context)
.go(SendLightningScreen.route, extra: destination);
}
}
});
}
Expand Down Expand Up @@ -181,7 +193,17 @@ class _ScannerScreenState extends State<ScannerScreen> {
_destination = destination;

if (_formKey.currentState!.validate()) {
GoRouter.of(context).go(SendScreen.route, extra: destination);
switch (destination!.getWalletType()) {
case WalletType.lightning:
GoRouter.of(context)
.go(SendLightningScreen.route, extra: destination);
case WalletType.onChain:
GoRouter.of(context)
.go(SendOnChainScreen.route, extra: destination);
case WalletType.stable:
GoRouter.of(context)
.go(SendLightningScreen.route, extra: destination);
}
}
});
},
Expand Down
143 changes: 103 additions & 40 deletions mobile/lib/features/wallet/send/confirm_payment_modal.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,24 @@ import 'package:flutter/material.dart';
import 'package:get_10101/common/color.dart';
import 'package:get_10101/common/domain/model.dart';
import 'package:get_10101/common/snack_bar.dart';
import 'package:get_10101/features/trade/domain/direction.dart';
import 'package:get_10101/features/trade/domain/leverage.dart';
import 'package:get_10101/features/trade/submit_order_change_notifier.dart';
import 'package:get_10101/features/trade/trade_value_change_notifier.dart';
import 'package:get_10101/features/wallet/application/util.dart';
import 'package:get_10101/features/wallet/domain/destination.dart';
import 'package:get_10101/features/wallet/domain/wallet_type.dart';
import 'package:get_10101/features/wallet/send/execute_payment_modal.dart';
import 'package:get_10101/features/wallet/send/payment_sent_change_notifier.dart';
import 'package:get_10101/features/wallet/send/send_dialog.dart';
import 'package:get_10101/features/wallet/wallet_change_notifier.dart';
import 'package:get_10101/logger/logger.dart';
import 'package:go_router/go_router.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:slide_to_confirm/slide_to_confirm.dart';

void showConfirmPaymentModal(BuildContext context, Destination destination, Amount? amount) {
void showConfirmPaymentModal(
BuildContext context, Destination destination, bool payWithUsdp, Amount sats, Amount usdp) {
showModalBottomSheet<void>(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
Expand All @@ -27,45 +33,110 @@ void showConfirmPaymentModal(BuildContext context, Destination destination, Amou
builder: (BuildContext context) {
return SingleChildScrollView(
child: SizedBox(
height: 320,
height: 420,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

of course 420, it's a multiple of 42!

child: Scaffold(
body: ConfirmPayment(
payWithUsdp: payWithUsdp,
destination: destination,
amount: amount,
sats: sats,
usdp: usdp,
))));
});
}

class ConfirmPayment extends StatelessWidget {
final Destination destination;
final Amount? amount;
final bool payWithUsdp;
final Amount sats;
final Amount usdp;

const ConfirmPayment({super.key, required this.destination, this.amount});
const ConfirmPayment(
{super.key,
required this.destination,
required this.payWithUsdp,
required this.sats,
required this.usdp});

@override
Widget build(BuildContext context) {
final walletService = context.read<WalletChangeNotifier>().service;
final submitOderChangeNotifier = context.read<SubmitOrderChangeNotifier>();
final formatter = NumberFormat("#,###,##0.00", "en");

final amt = destination.amount.sats > 0 ? destination.amount : amount!;
final tradeValuesChangeNotifier = context.watch<TradeValuesChangeNotifier>();

final tradeValues = tradeValuesChangeNotifier.fromDirection(Direction.long);
tradeValues.updateLeverage(Leverage(1));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need to update the leverage?

Copy link
Contributor Author

@holzeis holzeis Dec 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the default leverage is 2 and if I wouldn't set it to 1 we would get an incorrect sats amount from the quantity.


Amount amt = destination.amount;
if (destination.amount.sats == 0) {
if (payWithUsdp) {
// if the destination does not specify an amount and we ar paying with the usdp balance we
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙃

Suggested change
// if the destination does not specify an amount and we ar paying with the usdp balance we
// if the destination does not specify an amount and we are paying with the usdp balance we

// calculate the amount from the quantity point of view.
tradeValues.updateQuantity(usdp);
amt = tradeValues.margin!;
} else {
// Otherwise it is a regular lightning payment and we just pay the given amount.
amt = sats;
}
} else {
// if the amount is set on the invoice we need to pay the amount no matter what. That might
// lead to the usdp amount to jump by one dollar depending on the current bid price
tradeValues.updateMargin(destination.amount);
}

return SafeArea(
child: Padding(
child: Container(
color: Colors.white,
padding: const EdgeInsets.only(left: 20.0, top: 35.0, right: 20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Text("Destination:", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
const SizedBox(height: 2),
Text(truncateWithEllipsis(32, destination.raw), style: const TextStyle(fontSize: 16)),
const SizedBox(height: 20),
const Text("Payee:", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
const SizedBox(height: 2),
Text(truncateWithEllipsis(32, destination.payee), style: const TextStyle(fontSize: 16)),
const SizedBox(height: 20),
const Text("Amount:", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
const SizedBox(height: 2),
Text(amt.toString(), style: const TextStyle(fontSize: 16)),
const SizedBox(height: 25),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text("Summary", style: TextStyle(fontSize: 20)),
const SizedBox(height: 10),
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: tenTenOnePurple.shade200.withOpacity(0.1),
borderRadius: BorderRadius.circular(8)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Text("Amount", style: TextStyle(color: Colors.grey, fontSize: 16)),
const SizedBox(height: 5),
Visibility(
visible: payWithUsdp,
replacement: Text(amt.sats == 0 ? "Max" : amt.toString(),
style: const TextStyle(fontSize: 16)),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("~ \$ ${formatter.format(tradeValues.quantity?.toInt ?? 0)}",
style: const TextStyle(fontSize: 16)),
Text(amt.toString(),
style: const TextStyle(fontSize: 16, color: Colors.grey))
],
)),
const Divider(height: 40, indent: 0, endIndent: 0),
const Text("Destination", style: TextStyle(color: Colors.grey, fontSize: 16)),
const SizedBox(height: 5),
Text(truncateWithEllipsis(26, destination.raw),
style: const TextStyle(fontSize: 16)),
const Divider(height: 40, indent: 0, endIndent: 0),
const Text("Payee", style: TextStyle(color: Colors.grey, fontSize: 16)),
const SizedBox(height: 5),
Text(truncateWithEllipsis(26, destination.payee),
style: const TextStyle(fontSize: 16)),
const SizedBox(height: 10),
],
),
)
],
),
const SizedBox(height: 15),
ConfirmationSlider(
text: "Swipe to confirm",
textStyle: const TextStyle(color: Colors.black87),
Expand All @@ -77,30 +148,22 @@ class ConfirmPayment extends StatelessWidget {
size: 20,
),
onConfirmation: () async {
context.read<PaymentChangeNotifier>().waitForPayment();
GoRouter.of(context).pop();
final messenger = ScaffoldMessenger.of(context);
if (destination.getWalletType() == WalletType.lightning) {
showDialog(
context: context,
useRootNavigator: true,
barrierDismissible: false, // Prevent user from leaving
builder: (BuildContext context) {
return SendDialog(destination: destination, amount: amt);
});
}

walletService.sendPayment(destination, amt).then((value) {
if (destination.getWalletType() == WalletType.onChain) {
GoRouter.of(context).pop();
context.read<PaymentChangeNotifier>().waitForPayment();
if (payWithUsdp) {
submitOderChangeNotifier.submitPendingOrder(tradeValues, PositionAction.open);
}
}).catchError((error) {
logger.e("Failed to send payment: $error");
if (destination.getWalletType() == WalletType.onChain) {
showExecuteUsdpPaymentModal(context, destination, amt, payWithUsdp);
} else {
walletService.sendPayment(destination, amt).then((value) {
GoRouter.of(context).pop();
}).catchError((error) {
logger.e("Failed to send payment: $error");
showSnackBar(messenger, error.toString());
}
context.read<PaymentChangeNotifier>().failPayment();
});
});
}
})
],
),
Expand Down
Loading
Loading