2024-05-14 16:03:45 -07:00
|
|
|
import 'dart:async';
|
2024-02-16 15:37:08 -05:00
|
|
|
import 'dart:convert';
|
2024-02-08 14:45:21 -05:00
|
|
|
import 'dart:io';
|
|
|
|
|
2024-02-23 12:29:11 -05:00
|
|
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
2024-02-08 14:45:21 -05:00
|
|
|
import 'package:breez_sdk/breez_sdk.dart';
|
|
|
|
import 'package:breez_sdk/bridge_generated.dart';
|
2024-07-08 13:49:37 -07:00
|
|
|
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
|
2024-02-23 12:29:11 -05:00
|
|
|
import 'package:cw_bitcoin/electrum_wallet_snapshot.dart';
|
2024-02-08 14:45:21 -05:00
|
|
|
import 'package:cw_core/crypto_currency.dart';
|
2024-08-13 08:39:06 -07:00
|
|
|
import 'package:cw_core/encryption_file_utils.dart';
|
2024-02-16 15:37:08 -05:00
|
|
|
import 'package:cw_core/node.dart';
|
|
|
|
import 'package:cw_core/pathForWallet.dart';
|
|
|
|
import 'package:cw_core/pending_transaction.dart';
|
|
|
|
import 'package:cw_core/sync_status.dart';
|
2024-02-21 22:03:19 -05:00
|
|
|
import 'package:cw_core/transaction_direction.dart';
|
2024-07-10 11:00:49 -07:00
|
|
|
import 'package:cw_core/transaction_priority.dart';
|
2024-02-08 14:45:21 -05:00
|
|
|
import 'package:cw_core/unspent_coins_info.dart';
|
2024-02-16 15:37:08 -05:00
|
|
|
import 'package:cw_lightning/lightning_balance.dart';
|
2024-02-21 22:03:19 -05:00
|
|
|
import 'package:cw_lightning/lightning_transaction_info.dart';
|
2024-07-10 11:00:49 -07:00
|
|
|
import 'package:cw_lightning/lightning_transaction_priority.dart';
|
2024-02-08 14:45:21 -05:00
|
|
|
import 'package:hive/hive.dart';
|
|
|
|
import 'package:mobx/mobx.dart';
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
|
|
import 'package:cw_core/wallet_info.dart';
|
2024-02-08 14:59:06 -05:00
|
|
|
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
|
|
|
import 'package:cw_bitcoin/bitcoin_wallet_addresses.dart';
|
2024-02-08 14:45:21 -05:00
|
|
|
import 'package:cw_lightning/.secrets.g.dart' as secrets;
|
2024-03-04 09:47:39 -08:00
|
|
|
import 'package:cw_bitcoin/electrum_wallet.dart';
|
2024-08-12 12:17:15 -07:00
|
|
|
import 'package:blockchain_utils/blockchain_utils.dart';
|
2024-02-08 14:45:21 -05:00
|
|
|
|
2024-02-08 14:59:06 -05:00
|
|
|
part 'lightning_wallet.g.dart';
|
2024-02-08 14:45:21 -05:00
|
|
|
|
|
|
|
class LightningWallet = LightningWalletBase with _$LightningWallet;
|
|
|
|
|
2024-03-05 09:21:08 -08:00
|
|
|
abstract class LightningWalletBase extends ElectrumWallet with Store {
|
2024-03-04 09:47:39 -08:00
|
|
|
bool _isTransactionUpdating;
|
2024-02-16 15:37:08 -05:00
|
|
|
|
|
|
|
@override
|
|
|
|
@observable
|
|
|
|
SyncStatus syncStatus;
|
2024-02-15 17:27:05 -05:00
|
|
|
|
2024-02-23 12:29:11 -05:00
|
|
|
LightningWalletBase({
|
|
|
|
required String mnemonic,
|
|
|
|
required String password,
|
|
|
|
required WalletInfo walletInfo,
|
|
|
|
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
|
|
|
required Uint8List seedBytes,
|
2024-08-13 08:39:06 -07:00
|
|
|
required EncryptionFileUtils encryptionFileUtils,
|
2024-02-23 12:29:11 -05:00
|
|
|
String? addressPageType,
|
|
|
|
List<BitcoinAddressRecord>? initialAddresses,
|
|
|
|
LightningBalance? initialBalance,
|
|
|
|
Map<String, int>? initialRegularAddressIndex,
|
|
|
|
Map<String, int>? initialChangeAddressIndex,
|
2024-03-04 09:47:39 -08:00
|
|
|
}) : _isTransactionUpdating = false,
|
2024-02-16 15:37:08 -05:00
|
|
|
syncStatus = NotConnectedSyncStatus(),
|
2024-03-05 09:21:08 -08:00
|
|
|
_balance = ObservableMap<CryptoCurrency, LightningBalance>(),
|
2024-05-08 11:51:25 -07:00
|
|
|
mnemonic = mnemonic,
|
2024-07-02 11:16:57 -07:00
|
|
|
seedBytes = seedBytes,
|
2024-03-04 09:47:39 -08:00
|
|
|
super(
|
|
|
|
password: password,
|
|
|
|
walletInfo: walletInfo,
|
|
|
|
unspentCoinsInfo: unspentCoinsInfo,
|
2024-08-12 12:17:15 -07:00
|
|
|
network: BitcoinNetwork.mainnet,
|
2024-03-04 09:47:39 -08:00
|
|
|
initialAddresses: initialAddresses,
|
|
|
|
initialBalance: initialBalance,
|
|
|
|
seedBytes: seedBytes,
|
2024-08-13 08:39:06 -07:00
|
|
|
encryptionFileUtils: encryptionFileUtils,
|
2024-03-04 09:47:39 -08:00
|
|
|
currency: CryptoCurrency.btcln,
|
|
|
|
) {
|
2024-03-05 09:21:08 -08:00
|
|
|
_balance[CryptoCurrency.btcln] =
|
|
|
|
initialBalance ?? LightningBalance(confirmed: 0, unconfirmed: 0, frozen: 0);
|
2024-02-23 12:29:11 -05:00
|
|
|
walletAddresses = BitcoinWalletAddresses(
|
|
|
|
walletInfo,
|
|
|
|
initialAddresses: initialAddresses,
|
|
|
|
initialRegularAddressIndex: initialRegularAddressIndex,
|
|
|
|
initialChangeAddressIndex: initialChangeAddressIndex,
|
2024-08-12 12:17:15 -07:00
|
|
|
mainHd: super.hd,
|
|
|
|
sideHd: super.accountHD.childKey(Bip32KeyIndex(1)),
|
2024-03-04 09:47:39 -08:00
|
|
|
network: network,
|
2024-02-23 12:29:11 -05:00
|
|
|
);
|
2024-02-08 14:45:21 -05:00
|
|
|
|
|
|
|
autorun((_) {
|
|
|
|
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-03-05 09:21:08 -08:00
|
|
|
late final ObservableMap<CryptoCurrency, LightningBalance> _balance;
|
2024-05-14 16:03:45 -07:00
|
|
|
StreamSubscription<List<Payment>>? _paymentsSub;
|
|
|
|
StreamSubscription<NodeState?>? _nodeStateSub;
|
2024-06-27 12:29:00 -07:00
|
|
|
StreamSubscription<LogEntry>? _logStream;
|
2024-07-10 08:31:58 -07:00
|
|
|
StreamSubscription<InvoicePaidDetails>? _invoiceSub;
|
2024-07-03 11:03:55 -07:00
|
|
|
late final BreezSDK _sdk;
|
2024-03-05 09:21:08 -08:00
|
|
|
|
2024-07-02 11:16:57 -07:00
|
|
|
late final Uint8List seedBytes;
|
|
|
|
String mnemonic;
|
|
|
|
@override
|
|
|
|
String get seed => mnemonic;
|
|
|
|
|
2024-07-09 12:46:43 -07:00
|
|
|
Map<String, int> incomingPayments = <String, int>{};
|
2024-07-09 11:14:34 -07:00
|
|
|
|
2024-07-10 11:00:49 -07:00
|
|
|
RecommendedFees recommendedFees = RecommendedFees(
|
|
|
|
economyFee: 0,
|
|
|
|
fastestFee: 0,
|
|
|
|
halfHourFee: 0,
|
|
|
|
hourFee: 0,
|
|
|
|
minimumFee: 0,
|
|
|
|
);
|
|
|
|
|
2024-03-05 09:21:08 -08:00
|
|
|
@override
|
|
|
|
@computed
|
|
|
|
ObservableMap<CryptoCurrency, LightningBalance> get balance => _balance;
|
|
|
|
|
2024-03-04 09:47:39 -08:00
|
|
|
static Future<LightningWallet> create(
|
|
|
|
{required String mnemonic,
|
|
|
|
required String password,
|
|
|
|
required WalletInfo walletInfo,
|
|
|
|
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
2024-08-13 11:22:43 -07:00
|
|
|
required EncryptionFileUtils encryptionFileUtils,
|
2024-03-04 09:47:39 -08:00
|
|
|
String? addressPageType,
|
|
|
|
List<BitcoinAddressRecord>? initialAddresses,
|
|
|
|
LightningBalance? initialBalance,
|
|
|
|
Map<String, int>? initialRegularAddressIndex,
|
|
|
|
Map<String, int>? initialChangeAddressIndex}) async {
|
2024-02-08 14:59:06 -05:00
|
|
|
return LightningWallet(
|
2024-02-23 12:29:11 -05:00
|
|
|
mnemonic: mnemonic,
|
|
|
|
password: password,
|
|
|
|
walletInfo: walletInfo,
|
|
|
|
unspentCoinsInfo: unspentCoinsInfo,
|
|
|
|
initialAddresses: initialAddresses,
|
2024-03-04 09:47:39 -08:00
|
|
|
initialBalance: initialBalance,
|
2024-08-02 09:39:28 -07:00
|
|
|
seedBytes: await universalMnemonictoSeedBytes(
|
|
|
|
mnemonic,
|
|
|
|
derivationType: walletInfo.derivationInfo?.derivationType,
|
|
|
|
),
|
2024-08-13 11:22:43 -07:00
|
|
|
encryptionFileUtils: encryptionFileUtils,
|
2024-02-23 12:29:11 -05:00
|
|
|
initialRegularAddressIndex: initialRegularAddressIndex,
|
|
|
|
initialChangeAddressIndex: initialChangeAddressIndex,
|
2024-03-04 09:47:39 -08:00
|
|
|
addressPageType: addressPageType,
|
2024-02-23 12:29:11 -05:00
|
|
|
);
|
2024-02-08 14:45:21 -05:00
|
|
|
}
|
|
|
|
|
2024-02-08 14:59:06 -05:00
|
|
|
static Future<LightningWallet> open({
|
2024-02-08 14:45:21 -05:00
|
|
|
required String name,
|
|
|
|
required WalletInfo walletInfo,
|
|
|
|
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
|
|
|
required String password,
|
2024-08-13 08:39:06 -07:00
|
|
|
required EncryptionFileUtils encryptionFileUtils,
|
2024-02-08 14:45:21 -05:00
|
|
|
}) async {
|
2024-08-13 11:22:43 -07:00
|
|
|
final snp = await ElectrumWalletSnapshot.load(
|
|
|
|
encryptionFileUtils,
|
|
|
|
name,
|
|
|
|
walletInfo.type,
|
|
|
|
password,
|
|
|
|
BitcoinNetwork.mainnet,
|
|
|
|
);
|
2024-06-28 14:44:18 -07:00
|
|
|
|
2024-02-08 14:59:06 -05:00
|
|
|
return LightningWallet(
|
2024-05-08 11:51:25 -07:00
|
|
|
mnemonic: snp.mnemonic!,
|
2024-02-23 12:29:11 -05:00
|
|
|
password: password,
|
|
|
|
walletInfo: walletInfo,
|
|
|
|
unspentCoinsInfo: unspentCoinsInfo,
|
|
|
|
initialAddresses: snp.addresses,
|
2024-03-04 10:34:46 -08:00
|
|
|
initialBalance: LightningBalance(
|
|
|
|
confirmed: snp.balance.confirmed,
|
|
|
|
unconfirmed: snp.balance.unconfirmed,
|
|
|
|
frozen: snp.balance.frozen,
|
|
|
|
),
|
2024-08-02 09:39:28 -07:00
|
|
|
seedBytes: await universalMnemonictoSeedBytes(
|
|
|
|
snp.mnemonic!,
|
|
|
|
derivationType: walletInfo.derivationInfo?.derivationType,
|
|
|
|
),
|
2024-08-13 08:39:06 -07:00
|
|
|
encryptionFileUtils: encryptionFileUtils,
|
2024-02-23 12:29:11 -05:00
|
|
|
initialRegularAddressIndex: snp.regularAddressIndex,
|
|
|
|
initialChangeAddressIndex: snp.changeAddressIndex,
|
|
|
|
addressPageType: snp.addressPageType,
|
|
|
|
);
|
2024-02-08 14:45:21 -05:00
|
|
|
}
|
|
|
|
|
2024-05-30 11:02:57 -07:00
|
|
|
Future<void> _handleNodeState(NodeState? nodeState) async {
|
|
|
|
if (nodeState == null) return;
|
|
|
|
_balance[CryptoCurrency.btcln] = LightningBalance(
|
|
|
|
confirmed: nodeState.maxPayableMsat ~/ 1000,
|
|
|
|
unconfirmed: nodeState.maxReceivableMsat ~/ 1000,
|
2024-07-30 13:02:03 -07:00
|
|
|
frozen: nodeState.onchainBalanceMsat ~/ 1000,
|
2024-05-30 11:02:57 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> _handlePayments(List<Payment> payments) async {
|
|
|
|
_isTransactionUpdating = true;
|
|
|
|
final txs = convertToTxInfo(payments);
|
2024-07-08 13:49:37 -07:00
|
|
|
await alertIncomingTxs(txs);
|
2024-05-30 11:02:57 -07:00
|
|
|
transactionHistory.addMany(txs);
|
|
|
|
_isTransactionUpdating = false;
|
2024-07-12 08:58:04 -07:00
|
|
|
if (txs.isNotEmpty) {
|
|
|
|
await updateBalance();
|
|
|
|
}
|
2024-05-30 11:02:57 -07:00
|
|
|
}
|
|
|
|
|
2024-07-10 08:31:58 -07:00
|
|
|
Future<void> _handleInvoicePaid(InvoicePaidDetails details) async {
|
|
|
|
_isTransactionUpdating = true;
|
|
|
|
|
|
|
|
if (details.payment == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
final txs = convertToTxInfo([details.payment!]);
|
|
|
|
await alertIncomingTxs(txs);
|
|
|
|
transactionHistory.addMany(txs);
|
|
|
|
_isTransactionUpdating = false;
|
2024-07-12 08:58:04 -07:00
|
|
|
if (txs.isNotEmpty) {
|
|
|
|
await updateBalance();
|
|
|
|
}
|
2024-07-10 08:31:58 -07:00
|
|
|
}
|
|
|
|
|
2024-05-30 12:38:03 -07:00
|
|
|
@override
|
|
|
|
Future<void> renameWalletFiles(String newWalletName) async {
|
|
|
|
await stopBreez(true);
|
|
|
|
await super.renameWalletFiles(newWalletName);
|
2024-07-02 11:16:57 -07:00
|
|
|
await setupBreez(seedBytes);
|
2024-05-30 12:38:03 -07:00
|
|
|
}
|
|
|
|
|
2024-06-27 12:29:00 -07:00
|
|
|
void _logSdkEntries(LogEntry entry) {
|
|
|
|
switch (entry.level) {
|
|
|
|
case "ERROR":
|
|
|
|
case "WARN":
|
2024-07-08 10:05:10 -07:00
|
|
|
// case "INFO":
|
2024-06-28 14:44:18 -07:00
|
|
|
// case "DEBUG":
|
|
|
|
// case "TRACE":
|
2024-06-27 12:29:00 -07:00
|
|
|
print("BREEZ:${entry.level}: ${entry.line}");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-13 13:13:17 -05:00
|
|
|
Future<void> setupBreez(Uint8List seedBytes) async {
|
2024-07-08 10:05:10 -07:00
|
|
|
_sdk = await BreezSDK();
|
|
|
|
await _logStream?.cancel();
|
2024-07-03 11:03:55 -07:00
|
|
|
_logStream = _sdk.logStream.listen(_logSdkEntries);
|
2024-06-27 12:29:00 -07:00
|
|
|
|
2024-02-16 15:37:08 -05:00
|
|
|
try {
|
2024-07-03 11:03:55 -07:00
|
|
|
if (!(await _sdk.isInitialized())) {
|
|
|
|
_sdk.initialize();
|
2024-03-04 10:34:46 -08:00
|
|
|
}
|
2024-02-16 15:37:08 -05:00
|
|
|
} catch (e) {
|
|
|
|
print("Error initializing Breez: $e");
|
2024-05-20 09:12:18 -07:00
|
|
|
return;
|
2024-02-16 15:37:08 -05:00
|
|
|
}
|
2024-02-08 14:45:21 -05:00
|
|
|
|
2024-04-17 09:20:04 -07:00
|
|
|
GreenlightCredentials greenlightCredentials = GreenlightCredentials(
|
2024-06-25 10:51:13 -07:00
|
|
|
developerKey: base64.decode(secrets.greenlightKey),
|
|
|
|
developerCert: base64.decode(secrets.greenlightCert),
|
2024-04-17 09:20:04 -07:00
|
|
|
);
|
|
|
|
|
2024-02-13 13:13:17 -05:00
|
|
|
NodeConfig breezNodeConfig = NodeConfig.greenlight(
|
|
|
|
config: GreenlightNodeConfig(
|
2024-04-17 09:20:04 -07:00
|
|
|
partnerCredentials: greenlightCredentials,
|
|
|
|
inviteCode: null,
|
2024-02-13 13:13:17 -05:00
|
|
|
),
|
|
|
|
);
|
2024-07-03 11:03:55 -07:00
|
|
|
Config breezConfig = await _sdk.defaultConfig(
|
2024-02-13 13:13:17 -05:00
|
|
|
envType: EnvironmentType.Production,
|
|
|
|
apiKey: secrets.breezApiKey,
|
|
|
|
nodeConfig: breezNodeConfig,
|
|
|
|
);
|
2024-02-08 14:45:21 -05:00
|
|
|
|
2024-05-30 11:02:57 -07:00
|
|
|
String workingDir = await pathForWalletDir(name: walletInfo.name, type: type);
|
2024-05-31 09:29:37 -07:00
|
|
|
workingDir = "$workingDir/breez/";
|
2024-05-30 11:02:57 -07:00
|
|
|
|
2024-02-13 13:13:17 -05:00
|
|
|
new Directory(workingDir).createSync(recursive: true);
|
|
|
|
breezConfig = breezConfig.copyWith(workingDir: workingDir);
|
2024-02-20 12:02:52 -05:00
|
|
|
|
2024-05-02 14:09:28 -07:00
|
|
|
// disconnect if already connected
|
2024-02-16 15:37:08 -05:00
|
|
|
try {
|
2024-07-03 11:03:55 -07:00
|
|
|
if (await _sdk.isInitialized()) {
|
|
|
|
await _sdk.disconnect();
|
2024-05-17 12:47:11 -07:00
|
|
|
}
|
2024-05-14 16:03:45 -07:00
|
|
|
} catch (e, s) {
|
|
|
|
print("ERROR disconnecting from Breez: $e\n$s");
|
|
|
|
}
|
2024-02-20 12:02:52 -05:00
|
|
|
|
|
|
|
try {
|
2024-07-03 11:03:55 -07:00
|
|
|
await _sdk.connect(
|
2024-04-16 15:23:48 -07:00
|
|
|
req: ConnectRequest(
|
|
|
|
config: breezConfig,
|
|
|
|
seed: seedBytes,
|
|
|
|
),
|
|
|
|
);
|
2024-05-20 09:12:18 -07:00
|
|
|
} catch (e, s) {
|
|
|
|
print("Error connecting to Breez: $e\n$s");
|
2024-02-16 15:37:08 -05:00
|
|
|
}
|
2024-02-13 13:13:17 -05:00
|
|
|
|
2024-05-30 13:49:32 -07:00
|
|
|
await _nodeStateSub?.cancel();
|
2024-07-03 11:03:55 -07:00
|
|
|
_nodeStateSub = _sdk.nodeStateStream.listen((event) {
|
2024-05-30 11:02:57 -07:00
|
|
|
_handleNodeState(event);
|
2024-02-15 17:27:05 -05:00
|
|
|
});
|
2024-07-03 11:03:55 -07:00
|
|
|
await _handleNodeState(await _sdk.nodeInfo());
|
2024-02-15 17:27:05 -05:00
|
|
|
|
2024-05-30 13:49:32 -07:00
|
|
|
await _paymentsSub?.cancel();
|
2024-07-03 11:03:55 -07:00
|
|
|
_paymentsSub = _sdk.paymentsStream.listen((List<Payment> payments) {
|
2024-05-30 11:02:57 -07:00
|
|
|
_handlePayments(payments);
|
2024-02-22 12:32:06 -05:00
|
|
|
});
|
2024-07-03 11:03:55 -07:00
|
|
|
await _handlePayments(await _sdk.listPayments(req: ListPaymentsRequest()));
|
2024-02-21 22:03:19 -05:00
|
|
|
|
2024-07-10 08:31:58 -07:00
|
|
|
await _invoiceSub?.cancel();
|
|
|
|
_invoiceSub = _sdk.invoicePaidStream.listen((InvoicePaidDetails details) {
|
|
|
|
_handleInvoicePaid(details);
|
|
|
|
});
|
|
|
|
|
2024-07-03 11:03:55 -07:00
|
|
|
print("initialized breez: ${(await _sdk.isInitialized())}");
|
2024-02-08 14:45:21 -05:00
|
|
|
}
|
2024-02-16 15:37:08 -05:00
|
|
|
|
2024-05-23 12:20:22 -07:00
|
|
|
Future<void> stopBreez(bool disconnect) async {
|
|
|
|
if (disconnect) {
|
2024-07-03 11:03:55 -07:00
|
|
|
if (await _sdk.isInitialized()) {
|
|
|
|
await _sdk.disconnect();
|
2024-05-23 12:20:22 -07:00
|
|
|
}
|
2024-05-17 12:47:11 -07:00
|
|
|
}
|
2024-05-14 16:03:45 -07:00
|
|
|
await _nodeStateSub?.cancel();
|
|
|
|
await _paymentsSub?.cancel();
|
2024-07-15 13:42:35 -07:00
|
|
|
await _invoiceSub?.cancel();
|
|
|
|
await _logStream?.cancel();
|
2024-05-14 16:03:45 -07:00
|
|
|
}
|
|
|
|
|
2024-02-16 15:37:08 -05:00
|
|
|
@action
|
|
|
|
@override
|
|
|
|
Future<void> startSync() async {
|
|
|
|
try {
|
|
|
|
syncStatus = AttemptingSyncStatus();
|
2024-02-21 22:03:19 -05:00
|
|
|
await updateTransactions();
|
2024-07-10 11:00:49 -07:00
|
|
|
await fetchFees();
|
2024-02-16 15:37:08 -05:00
|
|
|
syncStatus = SyncedSyncStatus();
|
|
|
|
} catch (e) {
|
|
|
|
print(e);
|
|
|
|
syncStatus = FailedSyncStatus();
|
|
|
|
rethrow;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<void> changePassword(String password) {
|
|
|
|
throw UnimplementedError("changePassword");
|
|
|
|
}
|
|
|
|
|
2024-07-03 11:03:55 -07:00
|
|
|
@action
|
|
|
|
void _onConnectionStatusChange(bool? isConnected) {
|
|
|
|
if (syncStatus is SyncingSyncStatus) return;
|
|
|
|
|
|
|
|
if (isConnected == true && syncStatus is! SyncedSyncStatus) {
|
|
|
|
syncStatus = ConnectedSyncStatus();
|
|
|
|
} else if (isConnected == false) {
|
|
|
|
syncStatus = LostConnectionSyncStatus();
|
|
|
|
} else if (isConnected != true && syncStatus is! ConnectingSyncStatus) {
|
|
|
|
syncStatus = NotConnectedSyncStatus();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-16 15:37:08 -05:00
|
|
|
@action
|
|
|
|
@override
|
|
|
|
Future<void> connectToNode({required Node node}) async {
|
|
|
|
try {
|
|
|
|
syncStatus = ConnectingSyncStatus();
|
2024-02-21 22:03:19 -05:00
|
|
|
await updateTransactions();
|
2024-02-16 15:37:08 -05:00
|
|
|
syncStatus = ConnectedSyncStatus();
|
|
|
|
} catch (e) {
|
|
|
|
print(e);
|
|
|
|
syncStatus = FailedSyncStatus();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<PendingTransaction> createTransaction(Object credentials) async {
|
|
|
|
throw UnimplementedError("createTransaction");
|
|
|
|
}
|
|
|
|
|
2024-07-08 13:49:37 -07:00
|
|
|
Future<void> alertIncomingTxs(Map<String, LightningTransactionInfo> transactions) async {
|
|
|
|
for (var tx in transactions.values) {
|
|
|
|
// transaction is a receive that we haven't seen before:
|
|
|
|
if (tx.direction == TransactionDirection.incoming &&
|
|
|
|
transactionHistory.transactions[tx.id] == null) {
|
2024-07-09 12:46:43 -07:00
|
|
|
incomingPayments[tx.id] = tx.amount;
|
2024-07-08 13:49:37 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-21 22:03:19 -05:00
|
|
|
Future<bool> updateTransactions() async {
|
|
|
|
try {
|
|
|
|
if (_isTransactionUpdating) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
_isTransactionUpdating = true;
|
|
|
|
final transactions = await fetchTransactions();
|
2024-07-08 13:49:37 -07:00
|
|
|
await alertIncomingTxs(transactions);
|
2024-02-21 22:03:19 -05:00
|
|
|
transactionHistory.addMany(transactions);
|
|
|
|
await transactionHistory.save();
|
|
|
|
_isTransactionUpdating = false;
|
|
|
|
return true;
|
|
|
|
} catch (_) {
|
|
|
|
_isTransactionUpdating = false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-04 10:34:46 -08:00
|
|
|
Map<String, LightningTransactionInfo> convertToTxInfo(List<Payment> payments) {
|
|
|
|
Map<String, LightningTransactionInfo> transactions = {};
|
2024-02-21 22:03:19 -05:00
|
|
|
|
2024-02-22 12:47:29 -05:00
|
|
|
for (Payment tx in payments) {
|
2024-07-10 08:31:58 -07:00
|
|
|
bool pending = tx.status == PaymentStatus.Pending;
|
|
|
|
if (tx.status == PaymentStatus.Complete) {
|
|
|
|
pending = false;
|
|
|
|
}
|
|
|
|
|
2024-07-30 11:25:34 -07:00
|
|
|
bool isSend =
|
|
|
|
tx.paymentType == PaymentType.Sent || tx.paymentType == PaymentType.ClosedChannel;
|
2024-03-04 10:34:46 -08:00
|
|
|
transactions[tx.id] = LightningTransactionInfo(
|
2024-07-10 08:31:58 -07:00
|
|
|
isPending: pending,
|
2024-02-21 22:03:19 -05:00
|
|
|
id: tx.id,
|
|
|
|
amount: tx.amountMsat ~/ 1000,
|
2024-03-01 09:55:44 -08:00
|
|
|
fee: tx.feeMsat ~/ 1000,
|
2024-02-21 22:03:19 -05:00
|
|
|
date: DateTime.fromMillisecondsSinceEpoch(tx.paymentTime * 1000),
|
|
|
|
direction: isSend ? TransactionDirection.outgoing : TransactionDirection.incoming,
|
2024-07-30 11:25:34 -07:00
|
|
|
isChannelClose: tx.paymentType == PaymentType.ClosedChannel,
|
2024-02-21 22:03:19 -05:00
|
|
|
);
|
|
|
|
}
|
2024-02-22 12:32:06 -05:00
|
|
|
return transactions;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
2024-03-04 10:34:46 -08:00
|
|
|
Future<Map<String, LightningTransactionInfo>> fetchTransactions() async {
|
2024-07-03 11:03:55 -07:00
|
|
|
final payments = await _sdk.listPayments(req: ListPaymentsRequest());
|
2024-02-22 12:32:06 -05:00
|
|
|
final transactions = convertToTxInfo(payments);
|
2024-02-21 22:03:19 -05:00
|
|
|
|
2024-02-22 12:32:06 -05:00
|
|
|
return transactions;
|
2024-02-16 15:37:08 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
2024-05-29 16:40:57 -07:00
|
|
|
Future<void> rescan({
|
|
|
|
required int height,
|
|
|
|
int? chainTip,
|
|
|
|
ScanData? scanData,
|
|
|
|
bool? doSingleScan,
|
2024-06-13 11:19:38 -07:00
|
|
|
bool? usingElectrs,
|
2024-05-29 16:40:57 -07:00
|
|
|
}) async {
|
2024-07-09 12:46:43 -07:00
|
|
|
await updateTransactions();
|
2024-02-21 22:03:19 -05:00
|
|
|
}
|
2024-02-16 15:37:08 -05:00
|
|
|
|
2024-07-02 11:16:57 -07:00
|
|
|
@override
|
2024-02-16 15:37:08 -05:00
|
|
|
Future<void> init() async {
|
2024-07-09 11:14:34 -07:00
|
|
|
await super.init();
|
2024-07-02 11:16:57 -07:00
|
|
|
// initialize breez:
|
|
|
|
try {
|
|
|
|
await setupBreez(seedBytes);
|
|
|
|
} catch (e) {
|
|
|
|
print("Error initializing Breez: $e");
|
|
|
|
}
|
2024-02-16 15:37:08 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
String toJSON() => json.encode({
|
|
|
|
'mnemonic': mnemonic,
|
2024-02-23 12:29:11 -05:00
|
|
|
'account_index': walletAddresses.currentReceiveAddressIndexByType,
|
|
|
|
'change_address_index': walletAddresses.currentChangeAddressIndexByType,
|
|
|
|
'addresses': walletAddresses.allAddresses.map((addr) => addr.toJSON()).toList(),
|
|
|
|
'address_page_type': walletInfo.addressPageType == null
|
|
|
|
? SegwitAddresType.p2wpkh.toString()
|
|
|
|
: walletInfo.addressPageType.toString(),
|
|
|
|
'balance': balance[currency]?.toJSON(),
|
|
|
|
'network_type': network == BitcoinNetwork.testnet ? 'testnet' : 'mainnet',
|
2024-02-16 15:37:08 -05:00
|
|
|
});
|
|
|
|
|
2024-02-27 14:26:15 -05:00
|
|
|
Future<void> updateBalance() async {
|
2024-07-12 08:58:04 -07:00
|
|
|
await _handleNodeState(await _sdk.nodeInfo());
|
2024-02-27 14:26:15 -05:00
|
|
|
}
|
|
|
|
|
2024-02-16 15:37:08 -05:00
|
|
|
Future<String> makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type);
|
2024-05-14 16:03:45 -07:00
|
|
|
|
|
|
|
@override
|
2024-05-23 12:20:22 -07:00
|
|
|
Future<void> close({bool? switchingToSameWalletType}) async {
|
2024-05-14 16:03:45 -07:00
|
|
|
try {
|
|
|
|
await electrumClient.close();
|
|
|
|
} catch (_) {}
|
|
|
|
try {
|
2024-05-23 12:20:22 -07:00
|
|
|
bool shouldDisconnect = switchingToSameWalletType == null || !switchingToSameWalletType;
|
|
|
|
await stopBreez(shouldDisconnect);
|
2024-05-14 16:03:45 -07:00
|
|
|
} catch (e, s) {
|
|
|
|
print("Error stopping breez: $e\n$s");
|
|
|
|
}
|
|
|
|
}
|
2024-07-10 11:00:49 -07:00
|
|
|
|
|
|
|
Future<int> calculateEstimatedFeeAsync(TransactionPriority? priority, int? amount) async {
|
|
|
|
if (priority is LightningTransactionPriority) {
|
|
|
|
int feeRate = this.feeRate(priority);
|
|
|
|
return getEstimatedFeeWithFeeRate(feeRate, amount);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<int> getEstimatedFeeWithFeeRate(int feeRate, int? amount) async {
|
|
|
|
try {
|
2024-07-10 11:08:16 -07:00
|
|
|
if (amount == null) {
|
|
|
|
amount = 0;
|
2024-07-10 11:00:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
PrepareOnchainPaymentResponse prepareRes = await _sdk.prepareOnchainPayment(
|
|
|
|
req: PrepareOnchainPaymentRequest(
|
|
|
|
amountSat: amount,
|
|
|
|
amountType: SwapAmountType.Send,
|
|
|
|
claimTxFeerate: feeRate,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
|
|
|
print("Sender amount: ${prepareRes.senderAmountSat} sats");
|
|
|
|
print("Recipient amount: ${prepareRes.recipientAmountSat} sats");
|
|
|
|
print("Total fees: ${prepareRes.totalFees} sats");
|
|
|
|
return prepareRes.totalFees;
|
|
|
|
} catch (e) {
|
|
|
|
print("Error calculating fee: $e");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
int feeRate(TransactionPriority priority) {
|
|
|
|
try {
|
|
|
|
if (priority is LightningTransactionPriority) {
|
|
|
|
switch (priority) {
|
|
|
|
case LightningTransactionPriority.economy:
|
|
|
|
return recommendedFees.economyFee;
|
|
|
|
case LightningTransactionPriority.fastest:
|
|
|
|
return recommendedFees.fastestFee;
|
|
|
|
case LightningTransactionPriority.halfhour:
|
|
|
|
return recommendedFees.halfHourFee;
|
|
|
|
case LightningTransactionPriority.hour:
|
|
|
|
return recommendedFees.hourFee;
|
|
|
|
case LightningTransactionPriority.minimum:
|
|
|
|
return recommendedFees.minimumFee;
|
2024-07-10 11:08:16 -07:00
|
|
|
case LightningTransactionPriority.custom:
|
|
|
|
throw Exception("Use getEstimatedFeeWithFeeRate instead!");
|
2024-07-10 11:00:49 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
} catch (_) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> fetchFees() async {
|
|
|
|
recommendedFees = await _sdk.recommendedFees();
|
|
|
|
}
|
2024-02-08 14:45:21 -05:00
|
|
|
}
|