Skip to content

Commit

Permalink
Merge pull request #1706 from get10101/feat/send-usdp
Browse files Browse the repository at this point in the history
feat: Pay lightning invoice with USDP
  • Loading branch information
holzeis authored Dec 8, 2023
2 parents 980de39 + 05e96c0 commit 957d961
Show file tree
Hide file tree
Showing 14 changed files with 1,109 additions and 324 deletions.
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,
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));

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
// 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

0 comments on commit 957d961

Please sign in to comment.