This commit is contained in:
Matthew Fosse 2024-08-26 16:53:40 -04:00
parent 572cbfd365
commit 7f21831bbb
8 changed files with 261 additions and 206 deletions

View file

@ -56,7 +56,7 @@ dependency_overrides:
bitcoin_base:
git:
url: https://github.com/cake-tech/bitcoin_base
ref: cake-update-v5
ref: cake-update-v6
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

View file

@ -43,7 +43,7 @@ dependency_overrides:
bitcoin_base:
git:
url: https://github.com/cake-tech/bitcoin_base
ref: cake-update-v5
ref: cake-update-v6
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

View file

@ -223,14 +223,15 @@ abstract class LightningWalletBase extends ElectrumWallet with Store {
await setupLightningNode(mnemonic);
}
totalOnchainBalanceSats() async {
Future<void> updateBalance() async {
final balance = await _node?.listBalances();
if (balance == null) {
return;
}
_balance[CryptoCurrency.btcln] = LightningBalance(
confirmed: balance.spendableOnchainBalanceSats.toInt(),
unconfirmed: (balance.totalOnchainBalanceSats - balance.spendableOnchainBalanceSats).toInt(),
unconfirmed: 0,
// frozen: (balance.totalOnchainBalanceSats - balance.spendableOnchainBalanceSats).toInt(),
frozen: balance.totalOnchainBalanceSats.toInt(),
);
print("wallet balance: ${balance.totalOnchainBalanceSats}");
@ -260,25 +261,24 @@ abstract class LightningWalletBase extends ElectrumWallet with Store {
}
}
listPaymentsWithFilter(bool printPayments) async {
// final res =
// await aliceNode.listPaymentsWithFilter(paymentDirection: ldk.PaymentDirection.outbound);
// if (res.isNotEmpty) {
// if (printPayments) {
// if (kDebugMode) {
// print("======Payments========");
// for (var e in res) {
// print("amountMsat: ${e.amountMsat}");
// print("paymentId: ${e.id.field0}");
// print("status: ${e.status.name}");
// }
// }
// }
// return res.last;
// } else {
// return null;
// }
}
// listPaymentsWithFilter(bool printPayments) async {
// final res =
// await _node.listPaymentsWithFilter(paymentDirection: ldk.PaymentDirection.outbound);
// if (res.isEmpty) {
// return null;
// }
// if (printPayments) {
// if (kDebugMode) {
// print("======Payments========");
// for (var e in res) {
// print("amountMsat: ${e.amountMsat}");
// print("paymentId: ${e.id.field0}");
// print("status: ${e.status.name}");
// }
// }
// }
// return res.last;
// }
removeLastPayment() async {
// final lastPayment = await listPaymentsWithFilter(false);
@ -292,7 +292,7 @@ abstract class LightningWalletBase extends ElectrumWallet with Store {
Future<String> newOnchainAddress() async {
if (_node == null) {
return "";
throw Exception("Node is null!");
}
final payment = await _node!.onChainPayment();
final address = await payment.newAddress();
@ -336,14 +336,27 @@ abstract class LightningWalletBase extends ElectrumWallet with Store {
// ));
}
receiveAndSendPayments() async {
// final bobBolt11Handler = await bobNode.bolt11Payment();
Future<String> createInvoice({required BigInt amountMsat, String? description}) async {
if (_node == null) {
throw Exception("Node is null!");
}
final bolt11Handler = await _node!.bolt11Payment();
// final aliceBolt11Handler = await aliceNode.bolt11Payment();
// // Bob doesn't have a channel yet, so he can't receive normal payments,
// // but he can receive payments via JIT channels through an LSP configured
// // in its node.
// invoice = await bobBolt11Handler.receiveViaJitChannel(
// amountMsat: BigInt.from(25000 * 1000), description: 'asdf', expirySecs: 9217);
// check if we have an open channel:
final invoice = await bolt11Handler.receiveViaJitChannel(
amountMsat: amountMsat,
description: description ?? '',
expirySecs: 9000,
);
return invoice.signedRawInvoice;
// print(invoice!.signedRawInvoice);
// setState(() {
// displayText = invoice!.signedRawInvoice;
@ -355,12 +368,12 @@ abstract class LightningWalletBase extends ElectrumWallet with Store {
// });
}
stop() async {
Future<void> stop() async {
// await bobNode.stop();
// await aliceNode.stop();
}
Future handleEvent(ldk.Node node) async {
Future<void> handleEvent(ldk.Node node) async {
final res = await node.nextEvent();
res?.map(paymentSuccessful: (e) {
if (kDebugMode) {
@ -393,6 +406,7 @@ abstract class LightningWalletBase extends ElectrumWallet with Store {
}
});
await node.eventHandled();
handleEvent(_node!);
}
Future<void> startNode(ldk.Node node) async {
@ -417,22 +431,44 @@ abstract class LightningWalletBase extends ElectrumWallet with Store {
.setEsploraServer(esploraUrl)
.setStorageDirPath(workingDir)
.setListeningAddresses([address]);
// return ldk.Builder()
// .setEntropyBip39Mnemonic(mnemonic: ldk.Mnemonic(seedPhrase: mnemonic))
// .setEsploraServer(esploraUrl)
// .setStorageDirPath(workingDir)
// .setListeningAddresses([address])
// .setNetwork(ldk.Network.bitcoin)
// .setLiquiditySourceLsps2(address: address, publicKey: publicKey)
// .setGossipSourceRgs(DefaultServicesMutinynet.rgsServerUrl)
// .setLiquiditySourceLsps2(
// address: types.SocketAddress.hostname(
// addr: DefaultServicesMutinynet.lsps2SourceAddress,
// port: DefaultServicesMutinynet.lsps2SourcePort,
// ),
// publicKey: types.PublicKey(
// hex: DefaultServicesMutinynet.lsps2SourcePublicKey,
// ),
// token: DefaultServicesMutinynet.lsps2SourceToken,
// );
}
Future<String> sendToOnchainAddress({
required String address,
required BigInt amountSats,
}) async {
if (_node == null) {
throw Exception("Node is null!");
}
final addr = ldk.Address(s: address);
final payment = await _node!.onChainPayment();
final txid = await payment.sendToAddress(address: addr, amountSats: amountSats);
print('Sent On-Chain Txid: ${txid.hash}');
return txid.hash;
}
Future<void> setupLightningNode(String mnemonic) async {
// _sdk = await BreezSDK();
// await _logStream?.cancel();
// _logStream = _sdk.logStream.listen(_logSdkEntries);
// try {
// if (!(await _sdk.isInitialized())) {
// _sdk.initialize();
// }
// } catch (e) {
// print("Error initializing Breez: $e");
// return;
// }
if (_node != null) {
await _node?.stop();
}
@ -442,6 +478,11 @@ abstract class LightningWalletBase extends ElectrumWallet with Store {
await startNode(_node!);
print("node started!");
await handleEvent(_node!);
// Timer.periodic(Duration(seconds: 10), (timer) async {
// await handleEvent(_node!);
// });
// // disconnect if already connected
// try {
// if (await _sdk.isInitialized()) {
@ -500,6 +541,7 @@ abstract class LightningWalletBase extends ElectrumWallet with Store {
try {
syncStatus = AttemptingSyncStatus();
await updateTransactions();
await updateBalance();
await fetchFees();
syncStatus = SyncedSyncStatus();
} catch (e) {
@ -574,37 +616,43 @@ abstract class LightningWalletBase extends ElectrumWallet with Store {
}
}
// Map<String, LightningTransactionInfo> convertToTxInfo(List<Payment> payments) {
// Map<String, LightningTransactionInfo> transactions = {};
Map<String, LightningTransactionInfo> convertToTxInfo(List<ldk.PaymentDetails> payments) {
Map<String, LightningTransactionInfo> transactions = {};
// for (Payment tx in payments) {
// bool pending = tx.status == PaymentStatus.Pending;
// if (tx.status == PaymentStatus.Complete) {
// pending = false;
// }
for (final tx in payments) {
bool pending = tx.status == ldk.PaymentStatus.pending;
// bool isSend =
// tx.paymentType == PaymentType.Sent || tx.paymentType == PaymentType.ClosedChannel;
// transactions[tx.id] = LightningTransactionInfo(
// isPending: pending,
// id: tx.id,
// amount: tx.amountMsat ~/ 1000,
// fee: tx.feeMsat ~/ 1000,
// date: DateTime.fromMillisecondsSinceEpoch(tx.paymentTime * 1000),
// direction: isSend ? TransactionDirection.outgoing : TransactionDirection.incoming,
// isChannelClose: tx.paymentType == PaymentType.ClosedChannel,
// );
// }
// return transactions;
// }
bool isSend = tx.direction == ldk.PaymentDirection.outbound;
int amount = (tx.amountMsat?.toInt() ?? 0) ~/ 1000;
// TODO: check if this is a channel closure:
bool isChannelClose = false;
// if (tx.kind is ldk.PaymentKind.
transactions[tx.id.toString()] = LightningTransactionInfo(
isPending: pending,
id: tx.id.toString(),
amount: amount,
fee: 0, // not available
date: DateTime.fromMillisecondsSinceEpoch(tx.latestUpdateTimestamp.toInt() * 1000),
direction: isSend ? TransactionDirection.outgoing : TransactionDirection.incoming,
isChannelClose: isChannelClose,
);
}
return transactions;
}
@override
Future<Map<String, LightningTransactionInfo>> fetchTransactions() async {
// final payments = await _sdk.listPayments(req: ListPaymentsRequest());
// final transactions = convertToTxInfo(payments);
if (_node == null) {
return {};
}
// return transactions;
return {};
final payments = await _node!.listPayments();
print(payments);
final transactions = convertToTxInfo(payments);
return transactions;
}
@override
@ -622,9 +670,12 @@ abstract class LightningWalletBase extends ElectrumWallet with Store {
Future<void> init() async {
await super.init();
try {
await setupLightningNode(mnemonic);
print("starting lightning node!");
// await setupLightningNode(mnemonic);
await setupLightningNode(
"puppy interest whip tonight dad never sudden response push zone pig patch");
} catch (e) {
print("Error initializing Breez: $e");
print("Error initializing node: $e");
}
}
@ -640,10 +691,6 @@ abstract class LightningWalletBase extends ElectrumWallet with Store {
'network_type': network == BitcoinNetwork.testnet ? 'testnet' : 'mainnet',
});
Future<void> updateBalance() async {
// await _handleNodeState(await _sdk.nodeInfo());
}
Future<String> makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type);
@override

View file

@ -34,10 +34,6 @@ dependencies:
# url: https://github.com/breez/breez-sdk-flutter.git
# ref: v0.5.2
cryptography: ^2.0.5
bitcoin_base:
git:
url: https://github.com/cake-tech/bitcoin_base
ref: cake-update-v5
fluttertoast: 8.1.4
ldk_node: ^0.3.0
@ -55,9 +51,9 @@ dependency_overrides:
bitcoin_base:
git:
url: https://github.com/cake-tech/bitcoin_base
ref: cake-update-v5
ref: cake-update-v6
meta: 1.11.0
# flutter_rust_bridge: 2.0.0
flutter_rust_bridge: 2.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

View file

@ -211,13 +211,12 @@ class LightningSendPage extends BasePage {
),
SizedBox(height: 24),
Observer(builder: (_) {
final invoiceSats = 0;
// final invoiceSats = lightningSendViewModel.invoice?.amountMsat ?? null;
// if (invoiceSats != null) {
// _amountController.text = lightning!
// .bitcoinAmountToLightningString(amount: invoiceSats ~/ 1000)
// .replaceAll(",", "");
// }
final invoiceSats = lightningSendViewModel.invoice?.amountSat ?? null;
if (invoiceSats != null) {
_amountController.text = lightning!
.bitcoinAmountToLightningString(amount: invoiceSats.toInt())
.replaceAll(",", "");
}
return Column(
children: [
if (invoiceSats == null)
@ -242,7 +241,7 @@ class LightningSendPage extends BasePage {
.textFieldBorderTopPanelColor,
suffixIcon: SizedBox(width: 36),
initialValue:
"sats: ${lightning!.bitcoinAmountToLightningString(amount: invoiceSats ~/ 1000)}",
"sats: ${lightning!.bitcoinAmountToLightningString(amount: invoiceSats.toInt())}",
placeholderTextStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
@ -288,38 +287,37 @@ class LightningSendPage extends BasePage {
),
SizedBox(height: 12),
Observer(builder: (_) {
return SizedBox();
// if (lightningSendViewModel.invoice?.description?.isEmpty ?? true) {
// return SizedBox();
// }
if (lightningSendViewModel.invoice?.description?.isEmpty ?? true) {
return SizedBox();
}
// return Column(
// children: [
// BaseTextFormField(
// enabled: false,
// initialValue:
// "${S.of(context).description}: ${lightningSendViewModel.invoice?.description}",
// textInputAction: TextInputAction.next,
// borderColor: Theme.of(context)
// .extension<ExchangePageTheme>()!
// .textFieldBorderTopPanelColor,
// suffixIcon: SizedBox(width: 36),
// placeholderTextStyle: TextStyle(
// fontSize: 16,
// fontWeight: FontWeight.w600,
// color: Theme.of(context)
// .extension<ExchangePageTheme>()!
// .hintTextColor,
// ),
// textStyle: TextStyle(
// fontSize: 16,
// fontWeight: FontWeight.w600,
// color: Colors.white),
// validator: null,
// ),
// SizedBox(height: 12),
// ],
// );
return Column(
children: [
BaseTextFormField(
enabled: false,
initialValue:
"${S.of(context).description}: ${lightningSendViewModel.invoice?.description}",
textInputAction: TextInputAction.next,
borderColor: Theme.of(context)
.extension<ExchangePageTheme>()!
.textFieldBorderTopPanelColor,
suffixIcon: SizedBox(width: 36),
placeholderTextStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Theme.of(context)
.extension<ExchangePageTheme>()!
.hintTextColor,
),
textStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.white),
validator: null,
),
SizedBox(height: 12),
],
);
}),
if (lightningSendViewModel.btcAddress.isNotEmpty) ...[
Observer(
@ -459,19 +457,19 @@ class LightningSendPage extends BasePage {
);
String feeValue = '';
String feeFiatAmount = '';
// if (lightningSendViewModel.invoice != null) {
// output.address = lightningSendViewModel.invoice!.bolt11;
// output.cryptoAmount =
// "${lightningSendViewModel.satAmount.toString()} sats";
// } else if (lightningSendViewModel.btcAddress.isNotEmpty) {
// output.address = lightningSendViewModel.btcAddress;
// feeValue = lightningSendViewModel.estimatedFeeSats.toString();
// feeFiatAmount = lightningSendViewModel
// .formattedFiatAmount(lightningSendViewModel.estimatedFeeSats);
// output.cryptoAmount = "${_amountController.text} sats";
// } else {
// throw Exception("Input cannot be empty");
// }
if (lightningSendViewModel.invoice != null) {
output.address = lightningSendViewModel.invoice!.bolt11;
output.cryptoAmount =
"${lightningSendViewModel.satAmount.toString()} sats";
} else if (lightningSendViewModel.btcAddress.isNotEmpty) {
output.address = lightningSendViewModel.btcAddress;
feeValue = lightningSendViewModel.estimatedFeeSats.toString();
feeFiatAmount = lightningSendViewModel
.formattedFiatAmount(lightningSendViewModel.estimatedFeeSats);
output.cryptoAmount = "${_amountController.text} sats";
} else {
throw Exception("Input cannot be empty");
}
output.fiatAmount = lightningSendViewModel
.formattedFiatAmount(int.parse(_amountController.text));
bool cancel = await showPopUp<bool>(
@ -503,15 +501,16 @@ class LightningSendPage extends BasePage {
return;
}
// if (lightningSendViewModel.invoice != null) {
// await lightningSendViewModel.sendInvoice(
// lightningSendViewModel.invoice!,
// int.parse(_amountController.text));
// } else if (lightningSendViewModel.btcAddress.isNotEmpty) {
// await lightningSendViewModel.sendBtc(
// lightningSendViewModel.btcAddress,
// int.parse(_amountController.text));
// }
if (lightningSendViewModel.invoice != null) {
await lightningSendViewModel.sendInvoice(
lightningSendViewModel.invoice!,
int.parse(_amountController.text));
} else if (lightningSendViewModel.btcAddress.isNotEmpty) {
await lightningSendViewModel.sendBtc(
lightningSendViewModel.btcAddress,
BigInt.parse(_amountController.text),
);
}
await showPopUp<void>(
context: context,

View file

@ -1,7 +1,4 @@
import 'dart:async';
// import 'package:breez_sdk/breez_sdk.dart';
// import 'package:breez_sdk/bridge_generated.dart' as BZG;
// import 'package:breez_sdk/sdk.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/entities/calculate_fiat_amount_raw.dart';
import 'package:cake_wallet/entities/fiat_api_mode.dart';
@ -9,6 +6,7 @@ import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/lightning/lightning.dart';
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/view_model/lightning_view_model.dart';
import 'package:cw_core/balance.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/transaction_history.dart';
@ -16,6 +14,7 @@ import 'package:cw_core/transaction_info.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cw_lightning/lightning_wallet.dart';
import 'package:flutter/widgets.dart';
import 'package:mobx/mobx.dart';
import 'package:collection/collection.dart';
@ -70,8 +69,8 @@ abstract class LightningSendViewModelBase with Store {
@observable
String btcAddress = "";
// @observable
// LNInvoice? invoice;
@observable
LightningInvoice? invoice;
@action
void setLoading(bool value) {
@ -165,73 +164,78 @@ abstract class LightningSendViewModelBase with Store {
return amount;
}
// @action
// Future<void> sendInvoice(BZG.LNInvoice invoice, int satAmount) async {
// try {
// setLoading(true);
// late BZG.SendPaymentRequest req;
@action
Future<void> sendInvoice(LightningInvoice invoice, BigInt satAmount) async {
try {
setLoading(true);
// late BZG.SendPaymentRequest req;
// if (invoice.amountMsat == null) {
// req = BZG.SendPaymentRequest(
// bolt11: invoice.bolt11,
// amountMsat: satAmount * 1000,
// );
// } else {
// req = BZG.SendPaymentRequest(bolt11: invoice.bolt11);
// }
// if (invoice.amountMsat == null) {
// req = BZG.SendPaymentRequest(
// bolt11: invoice.bolt11,
// amountMsat: satAmount * 1000,
// );
// } else {
// req = BZG.SendPaymentRequest(bolt11: invoice.bolt11);
// }
// final response = await _sdk.sendPayment(req: req);
// if (response.payment.error != null) {
// throw Exception(response.payment.error);
// }
// final response = await _sdk.sendPayment(req: req);
// if (response.payment.error != null) {
// throw Exception(response.payment.error);
// }
// if (response.payment.status == BZG.PaymentStatus.Failed) {
// throw Exception("Payment failed");
// }
// if (response.payment.status == BZG.PaymentStatus.Failed) {
// throw Exception("Payment failed");
// }
// setLoading(false);
// } catch (e) {
// setLoading(false);
// rethrow;
// }
// }
throw Exception("Not implemented yet!");
setLoading(false);
} catch (e) {
setLoading(false);
rethrow;
}
}
@action
Future<void> sendBtc(String address, int satAmount) async {
// try {
// setLoading(true);
Future<void> sendBtc(String address, BigInt satAmount) async {
try {
setLoading(true);
// if (satAmount < minSats || (satAmount > maxSats && maxSats != 0)) {
// throw Exception("Amount is outside of liquidity limits!");
// }
// if (satAmount < minSats || (satAmount > maxSats && maxSats != 0)) {
// throw Exception("Amount is outside of liquidity limits!");
// }
// BZG.PrepareOnchainPaymentResponse prepareRes = await _sdk.prepareOnchainPayment(
// req: BZG.PrepareOnchainPaymentRequest(
// amountSat: satAmount,
// amountType: BZG.SwapAmountType.Send,
// claimTxFeerate: feeRate,
// ),
// );
// TODO: use proxy layer:
(wallet as LightningWallet).sendToOnchainAddress(address: address, amountSats: satAmount);
// print("Sender amount: ${prepareRes.senderAmountSat} sats");
// print("Recipient amount: ${prepareRes.recipientAmountSat} sats");
// print("Total fees: ${prepareRes.totalFees} sats");
// BZG.PrepareOnchainPaymentResponse prepareRes = await _sdk.prepareOnchainPayment(
// req: BZG.PrepareOnchainPaymentRequest(
// amountSat: satAmount,
// amountType: BZG.SwapAmountType.Send,
// claimTxFeerate: feeRate,
// ),
// );
// BZG.PayOnchainRequest req = BZG.PayOnchainRequest(
// recipientAddress: address,
// prepareRes: prepareRes,
// );
// BZG.PayOnchainResponse res = await _sdk.payOnchain(req: req);
// print("Sender amount: ${prepareRes.senderAmountSat} sats");
// print("Recipient amount: ${prepareRes.recipientAmountSat} sats");
// print("Total fees: ${prepareRes.totalFees} sats");
// if (res.reverseSwapInfo.status == BZG.ReverseSwapStatus.Cancelled) {
// throw Exception("Payment cancelled / error");
// }
// BZG.PayOnchainRequest req = BZG.PayOnchainRequest(
// recipientAddress: address,
// prepareRes: prepareRes,
// );
// BZG.PayOnchainResponse res = await _sdk.payOnchain(req: req);
// setLoading(false);
// } catch (e) {
// setLoading(false);
// rethrow;
// }
// if (res.reverseSwapInfo.status == BZG.ReverseSwapStatus.Cancelled) {
// throw Exception("Payment cancelled / error");
// }
setLoading(false);
} catch (e) {
setLoading(false);
rethrow;
}
}
int get feeRate {

View file

@ -177,3 +177,15 @@ class InvoiceSoftLimitsResult {
required this.balance,
});
}
class LightningInvoice {
final String bolt11;
final BigInt amountSat;
final String? description;
LightningInvoice({
required this.bolt11,
required this.amountSat,
this.description,
});
}

View file

@ -97,10 +97,6 @@ dependencies:
polyseed: ^0.0.6
nostr_tools: ^1.0.9
solana: ^0.30.1
bitcoin_base:
git:
url: https://github.com/cake-tech/bitcoin_base
ref: cake-update-v5
ledger_flutter: ^1.0.1
hashlib: ^1.19.2
ldk_node: ^0.3.0
@ -141,7 +137,8 @@ dependency_overrides:
bitcoin_base:
git:
url: https://github.com/cake-tech/bitcoin_base
ref: cake-update-v5
ref: cake-update-v6
flutter_rust_bridge: 2.0.0
flutter_icons:
image_path: "assets/images/app_logo.png"