Merge branch 'staging' into ui-testing

This commit is contained in:
julian 2022-09-08 08:03:57 -06:00
commit 9061b601c7
5 changed files with 162 additions and 76 deletions

View file

@ -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

View file

@ -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<void> _loadWalletBoxes() async {
final names = _boxAllWalletsData.get("names") as Map? ?? {};
names.removeWhere((name, dyn) {
final jsonObject = Map<String, dynamic>.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<String, dynamic>.from(names).map((name, dyn) => MapEntry(
name, WalletInfo.fromJson(Map<String, dynamic>.from(dyn as Map))));

View file

@ -731,6 +731,38 @@ Future<void> _setTestnetWrapper(bool isTestnet) async {
// setTestnet(isTestnet);
}
Future<bool> 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<String, dynamic>? 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

View file

@ -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);
}
}
}

View file

@ -126,6 +126,17 @@ class WalletsService extends ChangeNotifier {
}
Logging.instance.log("Fetched wallet names: $names", level: LogLevel.Info);
final mapped = Map<String, dynamic>.from(names);
mapped.removeWhere((name, dyn) {
final jsonObject = Map<String, dynamic>.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<String, dynamic>.from(dyn as Map))));