Skip to content

Commit

Permalink
Merge pull request #2606 from get10101/feat/mutiny-faucet-ln
Browse files Browse the repository at this point in the history
feat: use mutinynet faucet for channel opening fundings
  • Loading branch information
bonomat authored Jun 6, 2024
2 parents 182969a + 556286f commit f77a134
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:flutter/services.dart';
import 'package:get_10101/common/bitcoin_balance_field.dart';
import 'package:get_10101/common/color.dart';
import 'package:get_10101/common/countdown.dart';
import 'package:get_10101/bridge_generated/bridge_definitions.dart' as bridge;
import 'package:get_10101/common/custom_qr_code.dart';
import 'package:get_10101/common/domain/funding_channel_task.dart';
import 'package:get_10101/common/domain/model.dart';
Expand All @@ -13,6 +14,7 @@ import 'package:get_10101/features/trade/application/order_service.dart';
import 'package:get_10101/features/trade/channel_creation_flow/channel_configuration_screen.dart';
import 'package:get_10101/features/trade/submit_order_change_notifier.dart';
import 'package:get_10101/features/trade/trade_screen.dart';
import 'package:get_10101/features/wallet/application/faucet_service.dart';
import 'package:get_10101/features/wallet/application/util.dart';
import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart';
Expand Down Expand Up @@ -61,17 +63,24 @@ class ChannelFunding extends StatefulWidget {
class _ChannelFunding extends State<ChannelFunding> {
FundingType selectedBox = FundingType.onchain;

/// Should only be true if on signet
bool _showFaucet = false;
bool _isPayInvoiceButtonDisabled = false;

// TODO(holzeis): It would be nicer if this would come directly from the invoice.
final expiry = DateTime.timestamp().second + 300;

@override
Widget build(BuildContext context) {
final bridge.Config config = context.read<bridge.Config>();
final status = context.watch<FundingChannelChangeNotifier>().status;
final orderCreated = status == FundingChannelTaskStatus.orderCreated;

final qrCode = switch (selectedBox) {
FundingType.lightning => CustomQrCode(
data: widget.funding.paymentRequest ?? "https://x.com/get10101",
data: widget.funding.paymentRequest != null
? "lightning:${widget.funding.paymentRequest}"
: "https://x.com/get10101",
embeddedImage: widget.funding.paymentRequest != null
? const AssetImage("assets/10101_logo_icon_white_background.png")
: const AssetImage("assets/coming_soon.png"),
Expand All @@ -80,7 +89,7 @@ class _ChannelFunding extends State<ChannelFunding> {
dimension: 300,
),
FundingType.onchain => CustomQrCode(
// TODO: creating a bip21 qr code should be generic once we support other desposit methods
// TODO: creating a bip21 qr code should be generic once we support other deposit methods
data: "bitcoin:${widget.funding.bitcoinAddress}?amount=${widget.amount.btc.toString()}",
embeddedImage: const AssetImage("assets/10101_logo_icon_white_background.png"),
dimension: 300,
Expand Down Expand Up @@ -196,9 +205,49 @@ class _ChannelFunding extends State<ChannelFunding> {
ScaffoldMessenger.of(context), "Copied: ${qrCode.data}");
});
},
onDoubleTap: (config.network == "regtest" || config.network == "signet")
? () => setState(() => _showFaucet = !_showFaucet)
: null,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: qrCode,
child: _showFaucet
? Column(
children: [
const SizedBox(height: 125),
OutlinedButton(
onPressed: _isPayInvoiceButtonDisabled
? null
: () async {
setState(
() => _isPayInvoiceButtonDisabled = true);
final faucetService =
context.read<FaucetService>();
faucetService
.payInvoiceWithFaucet(
qrCode.data,
widget.amount,
config.network,
parseSelectedBox(selectedBox))
.catchError((error) {
setState(
() => _isPayInvoiceButtonDisabled = false);
showSnackBar(ScaffoldMessenger.of(context),
error.toString());
});
},
style: ElevatedButton.styleFrom(
shape: const RoundedRectangleBorder(
borderRadius:
BorderRadius.all(Radius.circular(5.0))),
),
child: config.network == "regtest"
? const Text("Pay with 10101 faucet")
: const Text("Pay with Mutinynet faucet"),
),
const SizedBox(height: 125),
],
)
: qrCode,
),
),
LayoutBuilder(
Expand Down Expand Up @@ -284,6 +333,7 @@ class _ChannelFunding extends State<ChannelFunding> {
onTap: () {
setState(() {
selectedBox = FundingType.unified;
_showFaucet = false;
});
},
),
Expand All @@ -296,6 +346,7 @@ class _ChannelFunding extends State<ChannelFunding> {
onTap: () {
setState(() {
selectedBox = FundingType.lightning;
_showFaucet = false;
});
},
),
Expand All @@ -308,6 +359,7 @@ class _ChannelFunding extends State<ChannelFunding> {
onTap: () {
setState(() {
selectedBox = FundingType.onchain;
_showFaucet = false;
});
},
),
Expand All @@ -320,6 +372,7 @@ class _ChannelFunding extends State<ChannelFunding> {
onTap: () {
setState(() {
selectedBox = FundingType.external;
_showFaucet = false;
});
},
),
Expand Down Expand Up @@ -512,3 +565,12 @@ class _RotatingIconState extends State<RotatingIcon> with SingleTickerProviderSt
);
}
}

Layer parseSelectedBox(FundingType fundingType) {
switch (fundingType) {
case FundingType.lightning:
return Layer.lightning;
default:
return Layer.onchain;
}
}
44 changes: 40 additions & 4 deletions mobile/lib/features/wallet/application/faucet_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ import 'package:get_10101/common/domain/model.dart';
import 'package:get_10101/logger/logger.dart';
import 'package:http/http.dart' as http;

enum Layer { onchain, lightning }

class FaucetService {
/// Pay the provided invoice with our faucet
Future<void> payInvoiceWithFaucet(String bip21Uri, Amount? invoiceAmount, String network) async {
Future<void> payInvoiceWithFaucet(
String bip21Uri, Amount? invoiceAmount, String network, Layer layer) async {
final split = bip21Uri.split(":");
final addressAndMaybeAmount = split[1].split("?");
logger.i("Funding $addressAndMaybeAmount");
Expand All @@ -18,10 +21,21 @@ class FaucetService {

switch (network) {
case "regtest":
await payWith10101Faucet(address, amount);
switch (layer) {
case Layer.onchain:
await payWith10101Faucet(address, amount);
case Layer.lightning:
throw Exception("We don't have a regtest faucet for LN");
}
break;
case "signet":
await payWithMutinyFaucet(address, amount);
switch (layer) {
case Layer.onchain:
await payWithMutinyOnChainFaucet(address, amount);
case Layer.lightning:
await payWithMutinyLightningFaucet(address);
}

break;
default:
throw Exception("Invalid network provided $network. Only regtest or signet supported");
Expand Down Expand Up @@ -80,7 +94,7 @@ class FaucetService {
}
}

Future<void> payWithMutinyFaucet(String address, double amountBtc) async {
Future<void> payWithMutinyOnChainFaucet(String address, double amountBtc) async {
final url = Uri.parse('https://faucet.mutinynet.com/api/onchain');
final headers = {
'Content-Type': 'application/json',
Expand Down Expand Up @@ -108,4 +122,26 @@ class FaucetService {
throw Exception("Failed funding address ${e.toString()}");
}
}

Future<void> payWithMutinyLightningFaucet(String bolt11) async {
final url = Uri.parse('https://faucet.mutinynet.com/api/lightning');
final headers = {
'Content-Type': 'application/json',
'Origin': 'https://faucet.mutinynet.com',
};
final body = jsonEncode({'bolt11': bolt11});

try {
final response = await http.post(url, headers: headers, body: body);

if (response.statusCode == 200) {
logger.i('Funding successful ${response.body}');
} else {
logger.e('Request failed with status: ${response.statusCode} ${response.body}');
throw Exception("Failed funding address ${response.statusCode} ${response.body}");
}
} catch (e) {
throw Exception("Failed funding address ${e.toString()}");
}
}
}
3 changes: 2 additions & 1 deletion mobile/lib/features/wallet/receive_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ class _ReceiveScreenState extends State<ReceiveScreen> {
setState(() => _isPayInvoiceButtonDisabled = true);
final faucetService = context.read<FaucetService>();
faucetService
.payInvoiceWithFaucet(rawInvoice(), amount, config.network)
.payInvoiceWithFaucet(
rawInvoice(), amount, config.network, Layer.onchain)
.catchError((error) {
setState(() => _isPayInvoiceButtonDisabled = false);
showSnackBar(ScaffoldMessenger.of(context), error.toString());
Expand Down

0 comments on commit f77a134

Please sign in to comment.