diff --git a/lib/services/coins/stellar/stellar_wallet.dart b/lib/services/coins/stellar/stellar_wallet.dart index 1dd8eae77..c4c92373f 100644 --- a/lib/services/coins/stellar/stellar_wallet.dart +++ b/lib/services/coins/stellar/stellar_wallet.dart @@ -1,30 +1,17 @@ import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; -import 'package:decimal/decimal.dart'; import 'package:http/http.dart' as http; import 'package:isar/isar.dart'; -import 'package:stackwallet/models/balance.dart' as SWBalance; -import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart' as SWTransaction; -import 'package:stackwallet/models/isar/models/blockchain_data/address.dart' as SWAddress; -import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart'; -import 'package:stackwallet/models/paymint/fee_object_model.dart'; -import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_balance_toggle_sheet.dart'; -import 'package:stackwallet/services/coins/coin_service.dart'; -import 'package:stackwallet/services/mixins/coin_control_interface.dart'; -import 'package:stackwallet/utilities/amount/amount.dart'; -import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart'; -import 'package:tuple/tuple.dart'; - import 'package:stackwallet/db/isar/main_db.dart'; +import 'package:stackwallet/models/balance.dart' as SWBalance; +import 'package:stackwallet/models/isar/models/blockchain_data/address.dart' + as SWAddress; +import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart' + as SWTransaction; +import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart'; import 'package:stackwallet/models/node_model.dart'; -import 'package:stackwallet/utilities/constants.dart'; -import 'package:stackwallet/utilities/default_nodes.dart'; -import 'package:stackwallet/utilities/enums/coin_enum.dart'; -import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'; -import 'package:stackwallet/utilities/logger.dart'; -import 'package:stackwallet/utilities/prefs.dart'; +import 'package:stackwallet/models/paymint/fee_object_model.dart'; +import 'package:stackwallet/services/coins/coin_service.dart'; import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart'; import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart'; import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart'; @@ -33,14 +20,20 @@ import 'package:stackwallet/services/mixins/wallet_cache.dart'; import 'package:stackwallet/services/mixins/wallet_db.dart'; import 'package:stackwallet/services/node_service.dart'; import 'package:stackwallet/services/transaction_notification_tracker.dart'; - +import 'package:stackwallet/utilities/amount/amount.dart'; +import 'package:stackwallet/utilities/constants.dart'; +import 'package:stackwallet/utilities/default_nodes.dart'; +import 'package:stackwallet/utilities/enums/coin_enum.dart'; +import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart'; +import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'; +import 'package:stackwallet/utilities/logger.dart'; +import 'package:stackwallet/utilities/prefs.dart'; import 'package:stellar_flutter_sdk/stellar_flutter_sdk.dart'; +import 'package:tuple/tuple.dart'; const int MINIMUM_CONFIRMATIONS = 1; -class StellarWallet extends CoinServiceAPI - with WalletCache, WalletDB, CoinControlInterface { - +class StellarWallet extends CoinServiceAPI with WalletCache, WalletDB { late StellarSDK stellarSdk; late Network stellarNetwork; @@ -116,7 +109,7 @@ class StellarWallet extends CoinServiceAPI // then periodically check _networkAliveTimer = Timer.periodic( Constants.networkAliveTimerDuration, - (_) async { + (_) async { _periodicPingCheck(); }, ); @@ -165,68 +158,68 @@ class StellarWallet extends CoinServiceAPI Coin get coin => _coin; late Coin _coin; - Future accoutExists(String accountId) async { + Future _accountExists(String accountId) async { bool exists = false; try { - AccountResponse receiverAccount = await stellarSdk.accounts - .account(accountId); + AccountResponse receiverAccount = + await stellarSdk.accounts.account(accountId); if (receiverAccount.accountId != "") { exists = true; } - } catch(e, s) { - Logging.instance.log("Error getting account ${e.toString()} - ${s.toString()}", level: LogLevel.Error); + } catch (e, s) { + Logging.instance.log( + "Error getting account ${e.toString()} - ${s.toString()}", + level: LogLevel.Error); } return exists; } + @override Future confirmSend({required Map txData}) async { - final secretSeed = await _secureStore.read( - key: '${_walletId}_secretSeed' - ); + final secretSeed = await _secureStore.read(key: '${_walletId}_secretSeed'); KeyPair senderKeyPair = KeyPair.fromSecretSeed(secretSeed!); - AccountResponse sender = await stellarSdk.accounts.account(senderKeyPair.accountId); + AccountResponse sender = + await stellarSdk.accounts.account(senderKeyPair.accountId); final amountToSend = txData['recipientAmt'] as Amount; //First check if account exists, can be skipped, but if the account does not exist, // the transaction fee will be charged when the transaction fails. - bool validAccount = await accoutExists(txData['address'] as String); + bool validAccount = await _accountExists(txData['address'] as String); Transaction transaction; if (!validAccount) { //Fund the account, user must ensure account is correct CreateAccountOperationBuilder createAccBuilder = - CreateAccountOperationBuilder( - txData['address'] as String, amountToSend.decimal.toString() - ); + CreateAccountOperationBuilder( + txData['address'] as String, amountToSend.decimal.toString()); transaction = TransactionBuilder(sender) .addOperation(createAccBuilder.build()) .build(); } else { transaction = TransactionBuilder(sender) - .addOperation(PaymentOperationBuilder( - txData['address'] as String, Asset.NATIVE, - amountToSend.decimal.toString()) - .build() - ).build(); + .addOperation(PaymentOperationBuilder(txData['address'] as String, + Asset.NATIVE, amountToSend.decimal.toString()) + .build()) + .build(); } transaction.sign(senderKeyPair, stellarNetwork); try { - SubmitTransactionResponse response = await stellarSdk.submitTransaction(transaction); + SubmitTransactionResponse response = + await stellarSdk.submitTransaction(transaction); if (!response.success) { - throw("Unable to send transaction"); + throw ("Unable to send transaction"); } return response.hash!; - } catch(e, s) { + } catch (e, s) { Logging.instance.log("Error sending TX $e - $s", level: LogLevel.Error); rethrow; } - } - Future get _currentReceivingAddress => - db.getAddresses(walletId) + Future get _currentReceivingAddress => db + .getAddresses(walletId) .filter() .typeEqualTo(SWAddress.AddressType.unknown) .and() @@ -242,7 +235,8 @@ class StellarWallet extends CoinServiceAPI // final nodeURI = Uri.parse("${getCurrentNode().host}:${getCurrentNode().port}"); final nodeURI = Uri.parse(getCurrentNode().host); final httpClient = http.Client(); - FeeStatsResponse fsp = await FeeStatsRequestBuilder(httpClient, nodeURI).execute(); + FeeStatsResponse fsp = + await FeeStatsRequestBuilder(httpClient, nodeURI).execute(); return int.parse(fsp.lastLedgerBaseFee); } @@ -279,8 +273,11 @@ class StellarWallet extends CoinServiceAPI NodeModel getCurrentNode() { if (_xlmNode != null) { return _xlmNode!; - } else if (NodeService(secureStorageInterface: _secureStore).getPrimaryNodeFor(coin: coin) != null) { - return NodeService(secureStorageInterface: _secureStore).getPrimaryNodeFor(coin: coin)!; + } else if (NodeService(secureStorageInterface: _secureStore) + .getPrimaryNodeFor(coin: coin) != + null) { + return NodeService(secureStorageInterface: _secureStore) + .getPrimaryNodeFor(coin: coin)!; } else { return DefaultNodes.getNodeFor(coin); } @@ -291,9 +288,9 @@ class StellarWallet extends CoinServiceAPI // final nodeURI = Uri.parse("${getCurrentNode().host}:${getCurrentNode().port}"); final nodeURI = Uri.parse(getCurrentNode().host); - final httpClient = http.Client(); - FeeStatsResponse fsp = await FeeStatsRequestBuilder(httpClient, nodeURI).execute(); + FeeStatsResponse fsp = + await FeeStatsRequestBuilder(httpClient, nodeURI).execute(); return FeeObject( numberOfBlocksFast: 0, @@ -301,12 +298,12 @@ class StellarWallet extends CoinServiceAPI numberOfBlocksSlow: 0, fast: int.parse(fsp.lastLedgerBaseFee) * 100, medium: int.parse(fsp.lastLedgerBaseFee) * 50, - slow: int.parse(fsp.lastLedgerBaseFee) * 10 - ); + slow: int.parse(fsp.lastLedgerBaseFee) * 10); } @override - Future fullRescan(int maxUnusedAddressGap, int maxNumberOfIndexesToCheck) async { + Future fullRescan( + int maxUnusedAddressGap, int maxNumberOfIndexesToCheck) async { await _prefs.init(); await updateTransactions(); await updateChainHeight(); @@ -338,25 +335,16 @@ class StellarWallet extends CoinServiceAPI await _prefs.init(); String mnemonic = await Wallet.generate24WordsMnemonic(); - await _secureStore.write( - key: '${_walletId}_mnemonic', - value: mnemonic - ); - await _secureStore.write( - key: '${_walletId}_mnemonicPassphrase', - value: "" - ); + await _secureStore.write(key: '${_walletId}_mnemonic', value: mnemonic); + await _secureStore.write(key: '${_walletId}_mnemonicPassphrase', value: ""); Wallet wallet = await Wallet.from(mnemonic); KeyPair keyPair = await wallet.getKeyPair(index: 0); String address = keyPair.accountId; - String secretSeed = keyPair.secretSeed; //This will be required for sending a tx + String secretSeed = + keyPair.secretSeed; //This will be required for sending a tx - - await _secureStore.write( - key: '${_walletId}_secretSeed', - value: secretSeed - ); + await _secureStore.write(key: '${_walletId}_secretSeed', value: secretSeed); final swAddress = SWAddress.Address( walletId: walletId, @@ -365,21 +353,16 @@ class StellarWallet extends CoinServiceAPI derivationIndex: 0, derivationPath: null, type: SWAddress.AddressType.unknown, // TODO: set type - subType: SWAddress.AddressSubType.unknown - ); - + subType: SWAddress.AddressSubType.unknown); + await db.putAddress(swAddress); - - await Future.wait([ - updateCachedId(walletId), - updateCachedIsFavorite(false) - ]); + + await Future.wait( + [updateCachedId(walletId), updateCachedIsFavorite(false)]); } Future getAddressSW() async { - var mnemonic = await _secureStore.read( - key: '${_walletId}_mnemonic' - ); + var mnemonic = await _secureStore.read(key: '${_walletId}_mnemonic'); Wallet wallet = await Wallet.from(mnemonic!); KeyPair keyPair = await wallet.getKeyPair(index: 0); @@ -400,16 +383,22 @@ class StellarWallet extends CoinServiceAPI Future get maxFee => throw UnimplementedError(); @override - Future> get mnemonic => mnemonicString.then((value) => value!.split(" ")); + Future> get mnemonic => + mnemonicString.then((value) => value!.split(" ")); @override - Future get mnemonicPassphrase => _secureStore.read(key: '${_walletId}_mnemonicPassphrase'); + Future get mnemonicPassphrase => + _secureStore.read(key: '${_walletId}_mnemonicPassphrase'); @override - Future get mnemonicString => _secureStore.read(key: '${_walletId}_mnemonic'); + Future get mnemonicString => + _secureStore.read(key: '${_walletId}_mnemonic'); @override - Future> prepareSend({required String address, required Amount amount, Map? args}) async { + Future> prepareSend( + {required String address, + required Amount amount, + Map? args}) async { try { final feeRate = args?["feeRate"]; var fee = 1000; @@ -440,13 +429,12 @@ class StellarWallet extends CoinServiceAPI } @override - Future recoverFromMnemonic({ - required String mnemonic, - String? mnemonicPassphrase, - required int maxUnusedAddressGap, - required int maxNumberOfIndexesToCheck, - required int height - }) async { + Future recoverFromMnemonic( + {required String mnemonic, + String? mnemonicPassphrase, + required int maxUnusedAddressGap, + required int maxNumberOfIndexesToCheck, + required int height}) async { if ((await mnemonicString) != null || (await this.mnemonicPassphrase) != null) { throw Exception("Attempted to overwrite mnemonic on restore!"); @@ -463,10 +451,7 @@ class StellarWallet extends CoinServiceAPI key: '${_walletId}_mnemonicPassphrase', value: mnemonicPassphrase ?? "", ); - await _secureStore.write( - key: '${_walletId}_secretSeed', - value: secretSeed - ); + await _secureStore.write(key: '${_walletId}_secretSeed', value: secretSeed); final swAddress = SWAddress.Address( walletId: walletId, @@ -475,15 +460,12 @@ class StellarWallet extends CoinServiceAPI derivationIndex: 0, derivationPath: null, type: SWAddress.AddressType.unknown, // TODO: set type - subType: SWAddress.AddressSubType.unknown - ); + subType: SWAddress.AddressSubType.unknown); await db.putAddress(swAddress); - await Future.wait([ - updateCachedId(walletId), - updateCachedIsFavorite(false) - ]); + await Future.wait( + [updateCachedId(walletId), updateCachedIsFavorite(false)]); } Future updateChainHeight() async { @@ -491,20 +473,22 @@ class StellarWallet extends CoinServiceAPI .order(RequestBuilderOrder.DESC) .limit(1) .execute() - .then((value) => value.records!.first.sequence).onError((error, stackTrace) => - throw("Error getting chain height")); + .then((value) => value.records!.first.sequence) + .onError((error, stackTrace) => throw ("Error getting chain height")); await updateCachedChainHeight(height); } Future updateTransactions() async { - try { - List> transactionList = []; + List> + transactionList = []; Page payments = await stellarSdk.payments - .forAccount(await getAddressSW()).order(RequestBuilderOrder.DESC) - .execute().onError((error, stackTrace) => - throw("Could not fetch transactions")); + .forAccount(await getAddressSW()) + .order(RequestBuilderOrder.DESC) + .execute() + .onError( + (error, stackTrace) => throw ("Could not fetch transactions")); for (OperationResponse response in payments.records!) { // PaymentOperationResponse por; @@ -515,8 +499,7 @@ class StellarWallet extends CoinServiceAPI "ALL TRANSACTIONS IS ${por.transactionSuccessful}", level: LogLevel.Info); - Logging.instance.log( - "THIS TX HASH IS ${por.transactionHash}", + Logging.instance.log("THIS TX HASH IS ${por.transactionHash}", level: LogLevel.Info); SWTransaction.TransactionType type; @@ -526,14 +509,18 @@ class StellarWallet extends CoinServiceAPI type = SWTransaction.TransactionType.incoming; } final amount = Amount( - rawValue: BigInt.parse(float.parse(por.amount!).toStringAsFixed(coin.decimals).replaceAll(".", "")), - fractionDigits: coin.decimals, - ); + rawValue: BigInt.parse(float + .parse(por.amount!) + .toStringAsFixed(coin.decimals) + .replaceAll(".", "")), + fractionDigits: coin.decimals, + ); int fee = 0; int height = 0; //Query the transaction linked to the payment, // por.transaction returns a null sometimes - TransactionResponse tx = await stellarSdk.transactions.transaction(por.transactionHash!); + TransactionResponse tx = + await stellarSdk.transactions.transaction(por.transactionHash!); if (tx.hash.isNotEmpty) { fee = tx.feeCharged!; @@ -542,7 +529,8 @@ class StellarWallet extends CoinServiceAPI var theTransaction = SWTransaction.Transaction( walletId: walletId, txid: por.transactionHash!, - timestamp: DateTime.parse(por.createdAt!).millisecondsSinceEpoch ~/ 1000, + timestamp: + DateTime.parse(por.createdAt!).millisecondsSinceEpoch ~/ 1000, type: type, subType: SWTransaction.TransactionSubType.none, amount: 0, @@ -559,18 +547,20 @@ class StellarWallet extends CoinServiceAPI numberOfMessages: null, ); SWAddress.Address? receivingAddress = await _currentReceivingAddress; - SWAddress.Address address = type == SWTransaction.TransactionType.incoming - ? receivingAddress! - : SWAddress.Address( - walletId: walletId, - value: por.sourceAccount!, - publicKey: KeyPair.fromAccountId(por.sourceAccount!).publicKey, - derivationIndex: 0, - derivationPath: null, - type: SWAddress.AddressType.unknown, // TODO: set type - subType: SWAddress.AddressSubType.unknown - ); - Tuple2 tuple = Tuple2(theTransaction, address); + SWAddress.Address address = + type == SWTransaction.TransactionType.incoming + ? receivingAddress! + : SWAddress.Address( + walletId: walletId, + value: por.sourceAccount!, + publicKey: + KeyPair.fromAccountId(por.sourceAccount!).publicKey, + derivationIndex: 0, + derivationPath: null, + type: SWAddress.AddressType.unknown, // TODO: set type + subType: SWAddress.AddressSubType.unknown); + Tuple2 tuple = + Tuple2(theTransaction, address); transactionList.add(tuple); } else if (response is CreateAccountOperationResponse) { CreateAccountOperationResponse caor = response; @@ -581,12 +571,16 @@ class StellarWallet extends CoinServiceAPI type = SWTransaction.TransactionType.incoming; } final amount = Amount( - rawValue: BigInt.parse(float.parse(caor.startingBalance!).toStringAsFixed(coin.decimals).replaceAll(".", "")), + rawValue: BigInt.parse(float + .parse(caor.startingBalance!) + .toStringAsFixed(coin.decimals) + .replaceAll(".", "")), fractionDigits: coin.decimals, ); int fee = 0; int height = 0; - TransactionResponse tx = await stellarSdk.transactions.transaction(caor.transactionHash!); + TransactionResponse tx = + await stellarSdk.transactions.transaction(caor.transactionHash!); if (tx.hash.isNotEmpty) { fee = tx.feeCharged!; height = tx.ledger; @@ -594,7 +588,8 @@ class StellarWallet extends CoinServiceAPI var theTransaction = SWTransaction.Transaction( walletId: walletId, txid: caor.transactionHash!, - timestamp: DateTime.parse(caor.createdAt!).millisecondsSinceEpoch ~/ 1000, + timestamp: + DateTime.parse(caor.createdAt!).millisecondsSinceEpoch ~/ 1000, type: type, subType: SWTransaction.TransactionSubType.none, amount: 0, @@ -611,17 +606,20 @@ class StellarWallet extends CoinServiceAPI numberOfMessages: null, ); SWAddress.Address? receivingAddress = await _currentReceivingAddress; - SWAddress.Address address = type == SWTransaction.TransactionType.incoming - ? receivingAddress! - : SWAddress.Address( - walletId: walletId, - value: caor.sourceAccount!, - publicKey: KeyPair.fromAccountId(caor.sourceAccount!).publicKey, - derivationIndex: 0, - derivationPath: null, - type: SWAddress.AddressType.unknown, // TODO: set type - subType: SWAddress.AddressSubType.unknown); - Tuple2 tuple = Tuple2(theTransaction, address); + SWAddress.Address address = + type == SWTransaction.TransactionType.incoming + ? receivingAddress! + : SWAddress.Address( + walletId: walletId, + value: caor.sourceAccount!, + publicKey: + KeyPair.fromAccountId(caor.sourceAccount!).publicKey, + derivationIndex: 0, + derivationPath: null, + type: SWAddress.AddressType.unknown, // TODO: set type + subType: SWAddress.AddressSubType.unknown); + Tuple2 tuple = + Tuple2(theTransaction, address); transactionList.add(tuple); } } @@ -631,12 +629,12 @@ class StellarWallet extends CoinServiceAPI "Exception rethrown from updateTransactions(): $e\n$s", level: LogLevel.Error); } - } Future updateBalance() async { try { - AccountResponse accountResponse = await stellarSdk.accounts.account(await getAddressSW()); + AccountResponse accountResponse = + await stellarSdk.accounts.account(await getAddressSW()); for (Balance balance in accountResponse.balances) { switch (balance.assetType) { case Asset.TYPE_NATIVE: @@ -662,9 +660,9 @@ class StellarWallet extends CoinServiceAPI await updateCachedBalance(_balance!); } } - } catch(e, s) { + } catch (e, s) { Logging.instance.log( - "ERROR GETTING BALANCE $e\$s", + "ERROR GETTING BALANCE $e\n$s", level: LogLevel.Info, ); } @@ -747,12 +745,13 @@ class StellarWallet extends CoinServiceAPI } @override - Future> get transactions => db.getTransactions(walletId).findAll(); + Future> get transactions => + db.getTransactions(walletId).findAll(); @override Future updateNode(bool shouldRefresh) async { _xlmNode = NodeService(secureStorageInterface: _secureStore) - .getPrimaryNodeFor(coin: coin) ?? + .getPrimaryNodeFor(coin: coin) ?? DefaultNodes.getNodeFor(coin); if (shouldRefresh) { @@ -784,14 +783,14 @@ class StellarWallet extends CoinServiceAPI ); final address = txData["address"] is String - ? await db.getAddress(walletId, txData["address"] as String) - : null; + ? await db.getAddress(walletId, txData["address"] as String) + : null; await db.addNewTransactionData( - [ - Tuple2(transaction, address), - ], - walletId, + [ + Tuple2(transaction, address), + ], + walletId, ); } @@ -807,4 +806,4 @@ class StellarWallet extends CoinServiceAPI @override String get walletId => _walletId; late String _walletId; -} \ No newline at end of file +} diff --git a/pubspec.lock b/pubspec.lock index 2987cff3e..cfdf7a710 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -956,7 +956,7 @@ packages: path: "crypto_plugins/flutter_liblelantus" relative: true source: path - version: "0.0.1" + version: "0.0.2" lints: dependency: transitive description: @@ -1365,62 +1365,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.2.1" - shared_preferences: - dependency: transitive - description: - name: shared_preferences - sha256: "0344316c947ffeb3a529eac929e1978fcd37c26be4e8468628bac399365a3ca1" - url: "https://pub.dev" - source: hosted - version: "2.2.0" - shared_preferences_android: - dependency: transitive - description: - name: shared_preferences_android - sha256: fe8401ec5b6dcd739a0fe9588802069e608c3fdbfd3c3c93e546cf2f90438076 - url: "https://pub.dev" - source: hosted - version: "2.2.0" - shared_preferences_foundation: - dependency: transitive - description: - name: shared_preferences_foundation - sha256: f39696b83e844923b642ce9dd4bd31736c17e697f6731a5adf445b1274cf3cd4 - url: "https://pub.dev" - source: hosted - version: "2.3.2" - shared_preferences_linux: - dependency: transitive - description: - name: shared_preferences_linux - sha256: "71d6806d1449b0a9d4e85e0c7a917771e672a3d5dc61149cc9fac871115018e1" - url: "https://pub.dev" - source: hosted - version: "2.3.0" - shared_preferences_platform_interface: - dependency: transitive - description: - name: shared_preferences_platform_interface - sha256: "23b052f17a25b90ff2b61aad4cc962154da76fb62848a9ce088efe30d7c50ab1" - url: "https://pub.dev" - source: hosted - version: "2.3.0" - shared_preferences_web: - dependency: transitive - description: - name: shared_preferences_web - sha256: "7347b194fb0bbeb4058e6a4e87ee70350b6b2b90f8ac5f8bd5b3a01548f6d33a" - url: "https://pub.dev" - source: hosted - version: "2.2.0" - shared_preferences_windows: - dependency: transitive - description: - name: shared_preferences_windows - sha256: f95e6a43162bce43c9c3405f3eb6f39e5b5d11f65fab19196cf8225e2777624d - url: "https://pub.dev" - source: hosted - version: "2.3.0" shelf: dependency: transitive description: @@ -1927,4 +1871,4 @@ packages: version: "1.0.0" sdks: dart: ">=3.0.2 <4.0.0" - flutter: ">=3.10.0" + flutter: ">=3.10.3"