diff --git a/README.md b/README.md index 7cd69b3f9..7c996db04 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,29 @@ [![codecov](https://codecov.io/gh/cypherstack/stack_wallet/branch/main/graph/badge.svg?token=PM1N56UTEW)](https://codecov.io/gh/cypherstack/stack_wallet) # Stack Wallet -put details here +Stack Wallet is a fully open source cryptocurrency wallet. With an easy to use user interface and quick and speedy transactions, this wallet is ideal for anyone no matter how much they know about the cryptocurrency space. The app is actively maintained to provide new user friendly features. [![Playstore](https://bluewallet.io/img/play-store-badge.svg)](https://play.google.com/store/apps/details?id=com.cypherstack.stackwallet) ## Feature List -put features here + +Highlights include: +- 5 Different cryptocurrencies +- All private keys and seeds stay on device and are never shared. +- Easy backup and restore feature to save all the information that's important to you. +- Trading cryptocurrencies through our partners. +- Custom address book +- Favorite wallets with fast syncing +- Custom Nodes. +- Open source software. ## Build and run ### Prerequisites -- Flutter 3.0.5 +- The only OS supported for building is Ubuntu 20.04 +- A machine with at least 100 GB of Storage +- Flutter 3.0.5 [(install manually or with git, do not install with snap)](https://docs.flutter.dev/get-started/install) - Dart SDK Requirement (>=2.17.0, up until <3.0.0) -- Android/iOS dev setup (Android Studio, xCode and subsequent dependencies) +- Android setup ([Android Studio](https://developer.android.com/studio) and subsequent dependencies) After that download the project and init the submodules ``` @@ -21,6 +32,19 @@ cd stack_wallet git submodule update --init --recursive ``` +You will need to install all dependencies listed in each of the plugins in the crypto_plugins folder. (eg. [Monero](https://github.com/cypherstack/flutter_libmonero), [Epic Cash](https://github.com/cypherstack/flutter_libepiccash) ) as of Sep 8th 2022 that is: + +Install [Rust](https://www.rust-lang.org/tools/install) +``` +cargo install cargo-ndk +rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android + +sudo apt install libc6-dev-i386 +sudo apt install build-essential cmake git libgit2-dev clang libncurses5-dev libncursesw5-dev zlib1g-dev pkg-config llvm +sudo apt install build-essential debhelper cmake libclang-dev libncurses5-dev clang libncursesw5-dev cargo rustc opencl-headers libssl-dev pkg-config ocl-icd-opencl-dev +sudo apt install unzip automake build-essential file pkg-config git python libtool libtinfo5 cmake openjdk-8-jre-headless +``` + Building plugins for Android ``` cd scripts/android/ @@ -29,15 +53,6 @@ cd scripts/android/ cd ../.. ``` -Building plugins for IOS - -``` -cd scripts/ios/ -./build_all.sh -// when finished go back to the root directory -cd ../.. -``` - Building plugins for testing on Linux ``` @@ -52,3 +67,5 @@ Finally, plug in your android device or use the emulator available via Android S flutter pub get flutter run ``` + +Note on Emulators: Only x86_64 emulators are supported, x86 emulators will not work diff --git a/lib/hive/db.dart b/lib/hive/db.dart index 79e98050b..3aae3096a 100644 --- a/lib/hive/db.dart +++ b/lib/hive/db.dart @@ -10,6 +10,8 @@ import 'package:stackwallet/models/trade_wallet_lookup.dart'; import 'package:stackwallet/services/wallets_service.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; +import 'package:stackwallet/utilities/logger.dart'; + class DB { static const String boxNameAddressBook = "addressBook"; static const String boxNameDebugInfo = "debugInfoBox"; @@ -141,6 +143,18 @@ class DB { Future _loadWalletBoxes() async { final names = _boxAllWalletsData.get("names") as Map? ?? {}; + names.removeWhere((name, dyn) { + final jsonObject = Map.from(dyn as Map); + try { + Coin.values.byName(jsonObject["coin"] as String); + return false; + } catch (e, s) { + Logging.instance.log( + "Error, ${jsonObject["coin"]} does not exist, $name wallet cannot be loaded", + level: LogLevel.Error); + return true; + } + }); final mapped = Map.from(names).map((name, dyn) => MapEntry( name, WalletInfo.fromJson(Map.from(dyn as Map)))); diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 0d5c1a6c3..df1061b33 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -731,6 +731,38 @@ Future _setTestnetWrapper(bool isTestnet) async { // setTestnet(isTestnet); } +Future getAnonymity(int groupID) async { + Logging.instance.log("getAnonymity", level: LogLevel.Info); + final Client client = Client(); + try { + final uri = Uri.parse("$kStackCommunityNodesEndpoint/getAnonymity"); + + final anonSetResult = await client.post( + uri, + headers: {'Content-Type': 'application/json'}, + body: jsonEncode({ + "jsonrpc": "2.0", + "id": "0", + 'index': groupID, + }), + ); + + // TODO: should the following be removed for security reasons in production? + Logging.instance + .log(anonSetResult.statusCode.toString(), level: LogLevel.Info); + Logging.instance.log(anonSetResult.body.toString(), level: LogLevel.Info); + final response = jsonDecode(anonSetResult.body.toString()); + if (response['status'] == 'success') { + return true; + } else { + return false; + } + } catch (e, s) { + Logging.instance.log("$e $s", level: LogLevel.Error); + return false; + } +} + /// Handles a single instance of a firo wallet class FiroWallet extends CoinServiceAPI { static const integrationTestFlag = @@ -1087,8 +1119,16 @@ class FiroWallet extends CoinServiceAPI { Map? args, }) async { try { + // check for send all + bool isSendAll = false; + final balance = + Format.decimalAmountToSatoshis(await availablePrivateBalance()); + if (satoshiAmount == balance) { + print("is send all"); + isSendAll = true; + } dynamic txHexOrError = - await _createJoinSplitTransaction(satoshiAmount, address, false); + await _createJoinSplitTransaction(satoshiAmount, address, isSendAll); Logging.instance.log("txHexOrError $txHexOrError", level: LogLevel.Error); if (txHexOrError is int) { // Here, we assume that transaction crafting returned an error diff --git a/lib/services/notifications_service.dart b/lib/services/notifications_service.dart index 31d8b664c..977d046a2 100644 --- a/lib/services/notifications_service.dart +++ b/lib/services/notifications_service.dart @@ -103,75 +103,79 @@ class NotificationsService extends ChangeNotifier { void _checkTransactions() async { for (final notification in _watchedTransactionNotifications) { - final Coin coin = coinFromPrettyName(notification.coinName); - final txid = notification.txid!; + try { + final Coin coin = coinFromPrettyName(notification.coinName); + final txid = notification.txid!; - final node = nodeService.getPrimaryNodeFor(coin: coin); - if (node != null) { - if (coin.isElectrumXCoin) { - final eNode = ElectrumXNode( - address: node.host, - port: node.port, - name: node.name, - id: node.id, - useSSL: node.useSSL, - ); - final failovers = nodeService - .failoverNodesFor(coin: coin) - .map((e) => ElectrumXNode( - address: e.host, - port: e.port, - name: e.name, - id: e.id, - useSSL: e.useSSL, - )) - .toList(); - - final client = ElectrumX.from( - node: eNode, - failovers: failovers, - prefs: prefs, - ); - final tx = await client.getTransaction(txHash: txid); - - int confirmations = tx["confirmations"] as int? ?? 0; - - bool shouldWatchForUpdates = true; - // check if the number of confirmations is greater than the number - // required by the wallet to count the tx as confirmed and update the - // flag on whether this notification should still be monitored - if (confirmations >= coin.requiredConfirmations) { - shouldWatchForUpdates = false; - confirmations = coin.requiredConfirmations; - } - - // grab confirms string to compare - final String newConfirms = - "($confirmations/${coin.requiredConfirmations})"; - final String oldConfirms = - notification.title.substring(notification.title.lastIndexOf("(")); - - // only update if they don't match - if (oldConfirms != newConfirms) { - final String newTitle = - notification.title.replaceFirst(oldConfirms, newConfirms); - - final updatedNotification = notification.copyWith( - title: newTitle, - shouldWatchForUpdates: shouldWatchForUpdates, + final node = nodeService.getPrimaryNodeFor(coin: coin); + if (node != null) { + if (coin.isElectrumXCoin) { + final eNode = ElectrumXNode( + address: node.host, + port: node.port, + name: node.name, + id: node.id, + useSSL: node.useSSL, ); + final failovers = nodeService + .failoverNodesFor(coin: coin) + .map((e) => ElectrumXNode( + address: e.host, + port: e.port, + name: e.name, + id: e.id, + useSSL: e.useSSL, + )) + .toList(); - // remove from watch list if shouldWatchForUpdates was changed - if (!shouldWatchForUpdates) { - await _deleteWatchedTxNotification(notification); + final client = ElectrumX.from( + node: eNode, + failovers: failovers, + prefs: prefs, + ); + final tx = await client.getTransaction(txHash: txid); + + int confirmations = tx["confirmations"] as int? ?? 0; + + bool shouldWatchForUpdates = true; + // check if the number of confirmations is greater than the number + // required by the wallet to count the tx as confirmed and update the + // flag on whether this notification should still be monitored + if (confirmations >= coin.requiredConfirmations) { + shouldWatchForUpdates = false; + confirmations = coin.requiredConfirmations; } - // replaces the current notification with the updated one - add(updatedNotification, true); + // grab confirms string to compare + final String newConfirms = + "($confirmations/${coin.requiredConfirmations})"; + final String oldConfirms = notification.title + .substring(notification.title.lastIndexOf("(")); + + // only update if they don't match + if (oldConfirms != newConfirms) { + final String newTitle = + notification.title.replaceFirst(oldConfirms, newConfirms); + + final updatedNotification = notification.copyWith( + title: newTitle, + shouldWatchForUpdates: shouldWatchForUpdates, + ); + + // remove from watch list if shouldWatchForUpdates was changed + if (!shouldWatchForUpdates) { + await _deleteWatchedTxNotification(notification); + } + + // replaces the current notification with the updated one + add(updatedNotification, true); + } + } else { + // TODO: check non electrumx coins } - } else { - // TODO: check non electrumx coins } + } catch (e, s) { + Logging.instance.log("$e $s", level: LogLevel.Error); } } } diff --git a/lib/services/wallets_service.dart b/lib/services/wallets_service.dart index a80bc2f82..b978f1edc 100644 --- a/lib/services/wallets_service.dart +++ b/lib/services/wallets_service.dart @@ -126,6 +126,17 @@ class WalletsService extends ChangeNotifier { } Logging.instance.log("Fetched wallet names: $names", level: LogLevel.Info); final mapped = Map.from(names); + mapped.removeWhere((name, dyn) { + final jsonObject = Map.from(dyn as Map); + try { + Coin.values.byName(jsonObject["coin"] as String); + return false; + } catch (e, s) { + Logging.instance.log("Error, ${jsonObject["coin"]} does not exist", + level: LogLevel.Error); + return true; + } + }); return mapped.map((name, dyn) => MapEntry( name, WalletInfo.fromJson(Map.from(dyn as Map))));