mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-01-23 02:54:30 +00:00
Merge branch 'staging' into desktop
This commit is contained in:
commit
baca31cfba
47 changed files with 3715 additions and 500 deletions
38
.github/workflows/test.yaml
vendored
38
.github/workflows/test.yaml
vendored
|
@ -50,48 +50,36 @@ jobs:
|
||||||
$encodedBytes = [System.Convert]::FromBase64String($env:CHANGE_NOW);
|
$encodedBytes = [System.Convert]::FromBase64String($env:CHANGE_NOW);
|
||||||
Set-Content $secretFileExchange -Value $encodedBytes -AsByteStream;
|
Set-Content $secretFileExchange -Value $encodedBytes -AsByteStream;
|
||||||
$secretFileExchangeHash = Get-FileHash $secretFileExchange;
|
$secretFileExchangeHash = Get-FileHash $secretFileExchange;
|
||||||
Write-Output "::set-output name=SECRET_FILE_EXCHANGE::$secretFileExchange";
|
|
||||||
Write-Output "::set-output name=SECRET_FILE_EXCHANGE_HASH::$($secretFileExchangeHash.Hash)";
|
|
||||||
Write-Output "Secret file $secretFileExchange has hash $($secretFileExchangeHash.Hash)";
|
Write-Output "Secret file $secretFileExchange has hash $($secretFileExchangeHash.Hash)";
|
||||||
|
|
||||||
$secretFileBitcoin = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/bitcoin/bitcoin_wallet_test_parameters.dart";
|
$secretFileBitcoin = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/bitcoin/bitcoin_wallet_test_parameters.dart";
|
||||||
$encodedBytes = [System.Convert]::FromBase64String($env:BITCOIN_TEST);
|
$encodedBytes = [System.Convert]::FromBase64String($env:BITCOIN_TEST);
|
||||||
Set-Content $secretFileBitcoin -Value $encodedBytes -AsByteStream;
|
Set-Content $secretFileBitcoin -Value $encodedBytes -AsByteStream;
|
||||||
$secretFileBitcoinHash = Get-FileHash $secretFileBitcoin;
|
$secretFileBitcoinHash = Get-FileHash $secretFileBitcoin;
|
||||||
Write-Output "::set-output name=SECRET_FILE_BITCOIN::$secretFileBitcoin";
|
|
||||||
Write-Output "::set-output name=SECRET_FILE_BITCOIN_HASH::$($secretFileBitcoinHash.Hash)";
|
|
||||||
Write-Output "Secret file $secretFileBitcoin has hash $($secretFileBitcoinHash.Hash)";
|
Write-Output "Secret file $secretFileBitcoin has hash $($secretFileBitcoinHash.Hash)";
|
||||||
|
|
||||||
$secretFileDogecoin = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/dogecoin/dogecoin_wallet_test_parameters.dart";
|
$secretFileDogecoin = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/dogecoin/dogecoin_wallet_test_parameters.dart";
|
||||||
$encodedBytes = [System.Convert]::FromBase64String($env:DOGECOIN_TEST);
|
$encodedBytes = [System.Convert]::FromBase64String($env:DOGECOIN_TEST);
|
||||||
Set-Content $secretFileDogecoin -Value $encodedBytes -AsByteStream;
|
Set-Content $secretFileDogecoin -Value $encodedBytes -AsByteStream;
|
||||||
$secretFileDogecoinHash = Get-FileHash $secretFileDogecoin;
|
$secretFileDogecoinHash = Get-FileHash $secretFileDogecoin;
|
||||||
Write-Output "::set-output name=SECRET_FILE_DOGECOIN::$secretFileDogecoin";
|
|
||||||
Write-Output "::set-output name=SECRET_FILE_DOGECOIN_HASH::$($secretFileDogecoinHash.Hash)";
|
|
||||||
Write-Output "Secret file $secretFileDogecoin has hash $($secretFileDogecoinHash.Hash)";
|
Write-Output "Secret file $secretFileDogecoin has hash $($secretFileDogecoinHash.Hash)";
|
||||||
|
|
||||||
$secretFileFiro = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/firo/firo_wallet_test_parameters.dart";
|
$secretFileFiro = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/firo/firo_wallet_test_parameters.dart";
|
||||||
$encodedBytes = [System.Convert]::FromBase64String($env:FIRO_TEST);
|
$encodedBytes = [System.Convert]::FromBase64String($env:FIRO_TEST);
|
||||||
Set-Content $secretFileFiro -Value $encodedBytes -AsByteStream;
|
Set-Content $secretFileFiro -Value $encodedBytes -AsByteStream;
|
||||||
$secretFileFiroHash = Get-FileHash $secretFileFiro;
|
$secretFileFiroHash = Get-FileHash $secretFileFiro;
|
||||||
Write-Output "::set-output name=SECRET_FILE_FIRO::$secretFileFiro";
|
|
||||||
Write-Output "::set-output name=SECRET_FILE_FIRO_HASH::$($secretFileFiroHash.Hash)";
|
|
||||||
Write-Output "Secret file $secretFileFiro has hash $($secretFileFiroHash.Hash)";
|
Write-Output "Secret file $secretFileFiro has hash $($secretFileFiroHash.Hash)";
|
||||||
|
|
||||||
$secretFileBitcoinCash = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/bitcoincash/bitcoincash_wallet_test_parameters.dart";
|
$secretFileBitcoinCash = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/bitcoincash/bitcoincash_wallet_test_parameters.dart";
|
||||||
$encodedBytes = [System.Convert]::FromBase64String($env:BITCOINCASH_TEST);
|
$encodedBytes = [System.Convert]::FromBase64String($env:BITCOINCASH_TEST);
|
||||||
Set-Content $secretFileBitcoinCash -Value $encodedBytes -AsByteStream;
|
Set-Content $secretFileBitcoinCash -Value $encodedBytes -AsByteStream;
|
||||||
$secretFileBitcoinCashHash = Get-FileHash $secretFileBitcoinCash;
|
$secretFileBitcoinCashHash = Get-FileHash $secretFileBitcoinCash;
|
||||||
Write-Output "::set-output name=SECRET_FILE_BITCOINCASH::$secretFileBitcoinCash";
|
|
||||||
Write-Output "::set-output name=SECRET_FILE_BITCOINCASH_HASH::$($secretFileBitcoinCashHash.Hash)";
|
|
||||||
Write-Output "Secret file $secretFileBitcoinCash has hash $($secretFileBitcoinCashHash.Hash)";
|
Write-Output "Secret file $secretFileBitcoinCash has hash $($secretFileBitcoinCashHash.Hash)";
|
||||||
|
|
||||||
$secretFileNamecoin = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/namecoin/namecoin_wallet_test_parameters.dart";
|
$secretFileNamecoin = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/namecoin/namecoin_wallet_test_parameters.dart";
|
||||||
$encodedBytes = [System.Convert]::FromBase64String($env:NAMECOIN_TEST);
|
$encodedBytes = [System.Convert]::FromBase64String($env:NAMECOIN_TEST);
|
||||||
Set-Content $secretFileNamecoin -Value $encodedBytes -AsByteStream;
|
Set-Content $secretFileNamecoin -Value $encodedBytes -AsByteStream;
|
||||||
$secretFileNamecoinHash = Get-FileHash $secretFileNamecoin;
|
$secretFileNamecoinHash = Get-FileHash $secretFileNamecoin;
|
||||||
Write-Output "::set-output name=SECRET_FILE_NAMECOIN::$secretFileNamecoin";
|
|
||||||
Write-Output "::set-output name=SECRET_FILE_NAMECOIN_HASH::$($secretFileNamecoinHash.Hash)";
|
|
||||||
Write-Output "Secret file $secretFileNamecoin has hash $($secretFileNamecoinHash.Hash)";
|
Write-Output "Secret file $secretFileNamecoin has hash $($secretFileNamecoinHash.Hash)";
|
||||||
|
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
@ -114,18 +102,18 @@ jobs:
|
||||||
file: coverage/lcov.info
|
file: coverage/lcov.info
|
||||||
- name: Delete temp files
|
- name: Delete temp files
|
||||||
run: |
|
run: |
|
||||||
Remove-Item -Path $env:CHANGE_NOW;
|
$secretFileExchange = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "lib/external_api_keys.dart";
|
||||||
Remove-Item -Path $env:BITCOIN_TEST;
|
$secretFileBitcoin = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/bitcoin/bitcoin_wallet_test_parameters.dart";
|
||||||
Remove-Item -Path $env:DOGECOIN_TEST;
|
$secretFileDogecoin = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/dogecoin/dogecoin_wallet_test_parameters.dart";
|
||||||
Remove-Item -Path $env:FIRO_TEST;
|
$secretFileFiro = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/firo/firo_wallet_test_parameters.dart";
|
||||||
Remove-Item -Path $env:BITCOINCASH_TEST;
|
$secretFileBitcoinCash = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/bitcoincash/bitcoincash_wallet_test_parameters.dart";
|
||||||
Remove-Item -Path $env:NAMECOIN_TEST;
|
$secretFileNamecoin = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/namecoin/namecoin_wallet_test_parameters.dart";
|
||||||
|
|
||||||
|
Remove-Item -Path $secretFileExchange;
|
||||||
|
Remove-Item -Path $secretFileBitcoin;
|
||||||
|
Remove-Item -Path $secretFileDogecoin;
|
||||||
|
Remove-Item -Path $secretFileFiro;
|
||||||
|
Remove-Item -Path $secretFileBitcoinCash;
|
||||||
|
Remove-Item -Path $secretFileNamecoin;
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
if: always()
|
if: always()
|
||||||
env:
|
|
||||||
CHANGE_NOW: ${{ steps.secret-file1.outputs.SECRET_FILE_EXCHANGE }}
|
|
||||||
BITCOIN_TEST: ${{ steps.secret-file1.outputs.SECRET_FILE_BITCOIN }}
|
|
||||||
DOGECOIN_TEST: ${{ steps.secret-file1.outputs.SECRET_FILE_DOGECOIN }}
|
|
||||||
FIRO_TEST: ${{ steps.secret-file1.outputs.SECRET_FILE_FIRO }}
|
|
||||||
BITCOINCASH_TEST: ${{ steps.secret-file1.outputs.SECRET_FILE_BITCOINCASH }}
|
|
||||||
NAMECOIN_TEST: ${{ steps.secret-file1.outputs.SECRET_FILE_NAMECOIN }}
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
<application
|
<application
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:label="Stack Wallet"
|
android:label="Stack Wallet"
|
||||||
|
android:requestLegacyExternalStorage="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
android:fullBackupContent="false">
|
android:fullBackupContent="false">
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.6.10'
|
ext.kotlin_version = '1.7.20'
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
jcenter()
|
||||||
|
|
|
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
|
||||||
|
|
|
@ -454,7 +454,7 @@
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 78;
|
CURRENT_PROJECT_VERSION = 79;
|
||||||
DEVELOPMENT_TEAM = 4DQKUWSG6C;
|
DEVELOPMENT_TEAM = 4DQKUWSG6C;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
@ -508,7 +508,7 @@
|
||||||
"$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**",
|
"$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**",
|
||||||
"$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs",
|
"$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.5.8;
|
MARKETING_VERSION = 1.5.9;
|
||||||
ONLY_ACTIVE_ARCH = NO;
|
ONLY_ACTIVE_ARCH = NO;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet;
|
PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
@ -641,7 +641,7 @@
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 78;
|
CURRENT_PROJECT_VERSION = 79;
|
||||||
DEVELOPMENT_TEAM = 4DQKUWSG6C;
|
DEVELOPMENT_TEAM = 4DQKUWSG6C;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
@ -695,7 +695,7 @@
|
||||||
"$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**",
|
"$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**",
|
||||||
"$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs",
|
"$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.5.8;
|
MARKETING_VERSION = 1.5.9;
|
||||||
ONLY_ACTIVE_ARCH = NO;
|
ONLY_ACTIVE_ARCH = NO;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet;
|
PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
@ -720,7 +720,7 @@
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 78;
|
CURRENT_PROJECT_VERSION = 79;
|
||||||
DEVELOPMENT_TEAM = 4DQKUWSG6C;
|
DEVELOPMENT_TEAM = 4DQKUWSG6C;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
@ -774,7 +774,7 @@
|
||||||
"$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**",
|
"$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**",
|
||||||
"$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs",
|
"$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.5.8;
|
MARKETING_VERSION = 1.5.9;
|
||||||
ONLY_ACTIVE_ARCH = NO;
|
ONLY_ACTIVE_ARCH = NO;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet;
|
PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:stackwallet/electrumx_rpc/electrumx.dart';
|
import 'package:stackwallet/electrumx_rpc/electrumx.dart';
|
||||||
import 'package:stackwallet/hive/db.dart';
|
import 'package:stackwallet/hive/db.dart';
|
||||||
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
|
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:stackwallet/utilities/logger.dart';
|
import 'package:stackwallet/utilities/logger.dart';
|
||||||
import 'package:stackwallet/utilities/prefs.dart';
|
import 'package:stackwallet/utilities/prefs.dart';
|
||||||
|
import 'package:string_validator/string_validator.dart';
|
||||||
|
|
||||||
class CachedElectrumX {
|
class CachedElectrumX {
|
||||||
final ElectrumX? electrumXClient;
|
final ElectrumX? electrumXClient;
|
||||||
|
@ -94,10 +97,32 @@ class CachedElectrumX {
|
||||||
|
|
||||||
// update set with new data
|
// update set with new data
|
||||||
if (newSet["setHash"] != "" && set["setHash"] != newSet["setHash"]) {
|
if (newSet["setHash"] != "" && set["setHash"] != newSet["setHash"]) {
|
||||||
set["setHash"] = newSet["setHash"];
|
set["setHash"] = !isHexadecimal(newSet["setHash"] as String)
|
||||||
set["blockHash"] = newSet["blockHash"];
|
? base64ToReverseHex(newSet["setHash"] as String)
|
||||||
|
: newSet["setHash"];
|
||||||
|
set["blockHash"] = !isHexadecimal(newSet["blockHash"] as String)
|
||||||
|
? base64ToHex(newSet["blockHash"] as String)
|
||||||
|
: newSet["blockHash"];
|
||||||
for (int i = (newSet["coins"] as List).length - 1; i >= 0; i--) {
|
for (int i = (newSet["coins"] as List).length - 1; i >= 0; i--) {
|
||||||
set["coins"].insert(0, newSet["coins"][i]);
|
dynamic newCoin = newSet["coins"][i];
|
||||||
|
List translatedCoin = [];
|
||||||
|
translatedCoin.add(!isHexadecimal(newCoin[0] as String)
|
||||||
|
? base64ToHex(newCoin[0] as String)
|
||||||
|
: newCoin[0]);
|
||||||
|
translatedCoin.add(!isHexadecimal(newCoin[1] as String)
|
||||||
|
? base64ToReverseHex(newCoin[1] as String)
|
||||||
|
: newCoin[1]);
|
||||||
|
try {
|
||||||
|
translatedCoin.add(!isHexadecimal(newCoin[2] as String)
|
||||||
|
? base64ToHex(newCoin[2] as String)
|
||||||
|
: newCoin[2]);
|
||||||
|
} catch (e, s) {
|
||||||
|
translatedCoin.add(newCoin[2]);
|
||||||
|
}
|
||||||
|
translatedCoin.add(!isHexadecimal(newCoin[3] as String)
|
||||||
|
? base64ToReverseHex(newCoin[3] as String)
|
||||||
|
: newCoin[3]);
|
||||||
|
set["coins"].insert(0, translatedCoin);
|
||||||
}
|
}
|
||||||
// save set to db
|
// save set to db
|
||||||
await DB.instance.put<dynamic>(
|
await DB.instance.put<dynamic>(
|
||||||
|
@ -118,6 +143,17 @@ class CachedElectrumX {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String base64ToHex(String source) =>
|
||||||
|
base64Decode(LineSplitter.split(source).join())
|
||||||
|
.map((e) => e.toRadixString(16).padLeft(2, '0'))
|
||||||
|
.join();
|
||||||
|
|
||||||
|
String base64ToReverseHex(String source) =>
|
||||||
|
base64Decode(LineSplitter.split(source).join())
|
||||||
|
.reversed
|
||||||
|
.map((e) => e.toRadixString(16).padLeft(2, '0'))
|
||||||
|
.join();
|
||||||
|
|
||||||
/// Call electrumx getTransaction on a per coin basis, storing the result in local db if not already there.
|
/// Call electrumx getTransaction on a per coin basis, storing the result in local db if not already there.
|
||||||
///
|
///
|
||||||
/// ElectrumX api only called if the tx does not exist in local db
|
/// ElectrumX api only called if the tx does not exist in local db
|
||||||
|
@ -189,7 +225,15 @@ class CachedElectrumX {
|
||||||
);
|
);
|
||||||
|
|
||||||
final serials = await client.getUsedCoinSerials(startNumber: startNumber);
|
final serials = await client.getUsedCoinSerials(startNumber: startNumber);
|
||||||
cachedSerials.addAll(serials["serials"] as List);
|
List newSerials = [];
|
||||||
|
for (var element in (serials["serials"] as List)) {
|
||||||
|
if (!isHexadecimal(element as String)) {
|
||||||
|
newSerials.add(base64ToHex(element));
|
||||||
|
} else {
|
||||||
|
newSerials.add(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cachedSerials.addAll(newSerials);
|
||||||
|
|
||||||
await DB.instance.put<dynamic>(
|
await DB.instance.put<dynamic>(
|
||||||
boxName: DB.instance.boxNameUsedSerialsCache(coin: coin),
|
boxName: DB.instance.boxNameUsedSerialsCache(coin: coin),
|
||||||
|
|
|
@ -9,6 +9,7 @@ import 'package:stackwallet/models/node_model.dart';
|
||||||
import 'package:stackwallet/models/notification_model.dart';
|
import 'package:stackwallet/models/notification_model.dart';
|
||||||
import 'package:stackwallet/models/trade_wallet_lookup.dart';
|
import 'package:stackwallet/models/trade_wallet_lookup.dart';
|
||||||
import 'package:stackwallet/services/wallets_service.dart';
|
import 'package:stackwallet/services/wallets_service.dart';
|
||||||
|
import 'package:stackwallet/utilities/constants.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:stackwallet/utilities/logger.dart';
|
import 'package:stackwallet/utilities/logger.dart';
|
||||||
|
|
||||||
|
@ -142,6 +143,17 @@ class DB {
|
||||||
_loadSharedCoinCacheBoxes(),
|
_loadSharedCoinCacheBoxes(),
|
||||||
]);
|
]);
|
||||||
_initialized = true;
|
_initialized = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (_boxPrefs.get("familiarity") == null) {
|
||||||
|
await _boxPrefs.put("familiarity", 0);
|
||||||
|
}
|
||||||
|
int count = _boxPrefs.get("familiarity") as int;
|
||||||
|
await _boxPrefs.put("familiarity", count + 1);
|
||||||
|
Constants.exchangeForExperiencedUsers(count + 1);
|
||||||
|
} catch (e, s) {
|
||||||
|
print("$e $s");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -146,7 +146,12 @@ void main() async {
|
||||||
boxName: DB.boxNameDBInfo, key: "hive_data_version") as int? ??
|
boxName: DB.boxNameDBInfo, key: "hive_data_version") as int? ??
|
||||||
0;
|
0;
|
||||||
if (dbVersion < Constants.currentHiveDbVersion) {
|
if (dbVersion < Constants.currentHiveDbVersion) {
|
||||||
await DbVersionMigrator().migrate(dbVersion);
|
try {
|
||||||
|
await DbVersionMigrator().migrate(dbVersion);
|
||||||
|
} catch (e, s) {
|
||||||
|
Logging.instance.log("Cannot migrate database\n$e $s",
|
||||||
|
level: LogLevel.Error, printFullLength: true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
monero.onStartup();
|
monero.onStartup();
|
||||||
|
@ -234,7 +239,9 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
||||||
// unawaited(_nodeService.updateCommunityNodes());
|
// unawaited(_nodeService.updateCommunityNodes());
|
||||||
|
|
||||||
// run without awaiting
|
// run without awaiting
|
||||||
if (Constants.enableExchange && _prefs.externalCalls) {
|
if (Constants.enableExchange &&
|
||||||
|
_prefs.externalCalls &&
|
||||||
|
await _prefs.isExternalCallsSet()) {
|
||||||
unawaited(ExchangeDataLoadingService().loadAll(ref));
|
unawaited(ExchangeDataLoadingService().loadAll(ref));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -276,6 +276,12 @@ class ExchangeFormState extends ChangeNotifier {
|
||||||
|
|
||||||
void _onExchangeRateTypeChanged() {
|
void _onExchangeRateTypeChanged() {
|
||||||
print("_onExchangeRateTypeChanged");
|
print("_onExchangeRateTypeChanged");
|
||||||
|
updateRanges(shouldNotifyListeners: true).then(
|
||||||
|
(_) => updateEstimate(
|
||||||
|
shouldNotifyListeners: true,
|
||||||
|
reversed: reversed,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onExchangeTypeChanged() {
|
void _onExchangeTypeChanged() {
|
||||||
|
|
|
@ -144,6 +144,8 @@ class _RestoreOptionsViewState extends ConsumerState<RestoreOptionsView> {
|
||||||
|
|
||||||
Future<void> chooseDate() async {
|
Future<void> chooseDate() async {
|
||||||
final height = MediaQuery.of(context).size.height;
|
final height = MediaQuery.of(context).size.height;
|
||||||
|
final fetchedColor =
|
||||||
|
Theme.of(context).extension<StackColors>()!.accentColorDark;
|
||||||
// check and hide keyboard
|
// check and hide keyboard
|
||||||
if (FocusScope.of(context).hasFocus) {
|
if (FocusScope.of(context).hasFocus) {
|
||||||
FocusScope.of(context).unfocus();
|
FocusScope.of(context).unfocus();
|
||||||
|
@ -155,8 +157,7 @@ class _RestoreOptionsViewState extends ConsumerState<RestoreOptionsView> {
|
||||||
initialDate: DateTime.now(),
|
initialDate: DateTime.now(),
|
||||||
height: height * 0.5,
|
height: height * 0.5,
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
primarySwatch: Util.createMaterialColor(
|
primarySwatch: Util.createMaterialColor(fetchedColor),
|
||||||
Theme.of(context).extension<StackColors>()!.accentColorDark),
|
|
||||||
),
|
),
|
||||||
//TODO pick a better initial date
|
//TODO pick a better initial date
|
||||||
// 2007 chosen as that is just before bitcoin launched
|
// 2007 chosen as that is just before bitcoin launched
|
||||||
|
@ -272,6 +273,7 @@ class _RestoreOptionsViewState extends ConsumerState<RestoreOptionsView> {
|
||||||
// if (!isDesktop)
|
// if (!isDesktop)
|
||||||
RestoreFromDatePicker(
|
RestoreFromDatePicker(
|
||||||
onTap: chooseDate,
|
onTap: chooseDate,
|
||||||
|
controller: _dateController,
|
||||||
),
|
),
|
||||||
|
|
||||||
// if (isDesktop)
|
// if (isDesktop)
|
||||||
|
|
|
@ -7,10 +7,14 @@ import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||||
import 'package:stackwallet/utilities/util.dart';
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
|
|
||||||
class RestoreFromDatePicker extends StatefulWidget {
|
class RestoreFromDatePicker extends StatefulWidget {
|
||||||
const RestoreFromDatePicker({Key? key, required this.onTap})
|
const RestoreFromDatePicker({
|
||||||
: super(key: key);
|
Key? key,
|
||||||
|
required this.onTap,
|
||||||
|
required this.controller,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
final VoidCallback onTap;
|
final VoidCallback onTap;
|
||||||
|
final TextEditingController controller;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<RestoreFromDatePicker> createState() => _RestoreFromDatePickerState();
|
State<RestoreFromDatePicker> createState() => _RestoreFromDatePickerState();
|
||||||
|
@ -23,17 +27,11 @@ class _RestoreFromDatePickerState extends State<RestoreFromDatePicker> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
onTap = widget.onTap;
|
onTap = widget.onTap;
|
||||||
_dateController = TextEditingController();
|
_dateController = widget.controller;
|
||||||
|
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_dateController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:device_info_plus/device_info_plus.dart';
|
||||||
import 'package:event_bus/event_bus.dart';
|
import 'package:event_bus/event_bus.dart';
|
||||||
import 'package:file_picker/file_picker.dart';
|
import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:stackwallet/models/isar/models/log.dart';
|
import 'package:stackwallet/models/isar/models/log.dart';
|
||||||
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
||||||
|
@ -24,6 +28,13 @@ import 'package:stackwallet/widgets/rounded_container.dart';
|
||||||
import 'package:stackwallet/widgets/stack_dialog.dart';
|
import 'package:stackwallet/widgets/stack_dialog.dart';
|
||||||
import 'package:stackwallet/widgets/stack_text_field.dart';
|
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||||
import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
||||||
|
import 'package:flutter_libepiccash/git_versions.dart' as EPIC_VERSIONS;
|
||||||
|
import 'package:flutter_libmonero/git_versions.dart' as MONERO_VERSIONS;
|
||||||
|
import 'package:lelantus/git_versions.dart' as FIRO_VERSIONS;
|
||||||
|
|
||||||
|
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/helpers/stack_file_system.dart';
|
||||||
|
|
||||||
|
import 'package:stackwallet/utilities/clipboard_interface.dart';
|
||||||
|
|
||||||
import 'package:stackwallet/utilities/util.dart';
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
|
|
||||||
|
@ -272,21 +283,77 @@ class _DebugViewState extends ConsumerState<DebugView> {
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
// BlueTextButton(
|
BlueTextButton(
|
||||||
// text: ref.watch(debugServiceProvider
|
text: "Save Debug Info to clipboard",
|
||||||
// .select((value) => value.isPaused))
|
onTap: () async {
|
||||||
// ? "Unpause"
|
try {
|
||||||
// : "Pause",
|
final packageInfo =
|
||||||
// onTap: () {
|
await PackageInfo.fromPlatform();
|
||||||
// ref
|
final version = packageInfo.version;
|
||||||
// .read(debugServiceProvider)
|
final build = packageInfo.buildNumber;
|
||||||
// .togglePauseUiUpdates();
|
final signature = packageInfo.buildSignature;
|
||||||
// },
|
final appName = packageInfo.appName;
|
||||||
// ),
|
String firoCommit =
|
||||||
|
FIRO_VERSIONS.getPluginVersion();
|
||||||
|
String epicCashCommit =
|
||||||
|
EPIC_VERSIONS.getPluginVersion();
|
||||||
|
String moneroCommit =
|
||||||
|
MONERO_VERSIONS.getPluginVersion();
|
||||||
|
DeviceInfoPlugin deviceInfoPlugin =
|
||||||
|
DeviceInfoPlugin();
|
||||||
|
final deviceInfo =
|
||||||
|
await deviceInfoPlugin.deviceInfo;
|
||||||
|
var deviceInfoMap = deviceInfo.toMap();
|
||||||
|
deviceInfoMap.remove("systemFeatures");
|
||||||
|
|
||||||
|
final logs = filtered(
|
||||||
|
ref.watch(debugServiceProvider.select(
|
||||||
|
(value) => value.recentLogs)),
|
||||||
|
_searchTerm)
|
||||||
|
.reversed
|
||||||
|
.toList(growable: false);
|
||||||
|
List errorLogs = [];
|
||||||
|
for (var log in logs) {
|
||||||
|
if (log.logLevel == LogLevel.Error ||
|
||||||
|
log.logLevel == LogLevel.Fatal) {
|
||||||
|
errorLogs.add(
|
||||||
|
"${log.logLevel}: ${log.message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final finalDebugMap = {
|
||||||
|
"version": version,
|
||||||
|
"build": build,
|
||||||
|
"signature": signature,
|
||||||
|
"appName": appName,
|
||||||
|
"firoCommit": firoCommit,
|
||||||
|
"epicCashCommit": epicCashCommit,
|
||||||
|
"moneroCommit": moneroCommit,
|
||||||
|
"deviceInfoMap": deviceInfoMap,
|
||||||
|
"errorLogs": errorLogs,
|
||||||
|
};
|
||||||
|
Logging.instance.log(
|
||||||
|
json.encode(finalDebugMap),
|
||||||
|
level: LogLevel.Info,
|
||||||
|
printFullLength: true);
|
||||||
|
const ClipboardInterface clipboard =
|
||||||
|
ClipboardWrapper();
|
||||||
|
await clipboard.setData(
|
||||||
|
ClipboardData(
|
||||||
|
text: json.encode(finalDebugMap)),
|
||||||
|
);
|
||||||
|
} catch (e, s) {
|
||||||
|
Logging.instance
|
||||||
|
.log("$e $s", level: LogLevel.Error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
BlueTextButton(
|
BlueTextButton(
|
||||||
text: "Save logs to file",
|
text: "Save logs to file",
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
|
final systemfile = StackFileSystem();
|
||||||
|
await systemfile.prepareStorage();
|
||||||
Directory rootPath =
|
Directory rootPath =
|
||||||
(await getApplicationDocumentsDirectory());
|
(await getApplicationDocumentsDirectory());
|
||||||
|
|
||||||
|
@ -313,8 +380,9 @@ class _DebugViewState extends ConsumerState<DebugView> {
|
||||||
} else {
|
} else {
|
||||||
path = await FilePicker.platform
|
path = await FilePicker.platform
|
||||||
.getDirectoryPath(
|
.getDirectoryPath(
|
||||||
dialogTitle: "Choose Backup location",
|
dialogTitle: "Choose Log Save Location",
|
||||||
initialDirectory: dir.path,
|
initialDirectory:
|
||||||
|
systemfile.startPath!.path,
|
||||||
lockParentWindow: true,
|
lockParentWindow: true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -336,9 +404,17 @@ class _DebugViewState extends ConsumerState<DebugView> {
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
|
|
||||||
final filename = await ref
|
bool logssaved = true;
|
||||||
.read(debugServiceProvider)
|
var filename;
|
||||||
.exportToFile(path, eventBus);
|
try {
|
||||||
|
filename = await ref
|
||||||
|
.read(debugServiceProvider)
|
||||||
|
.exportToFile(path, eventBus);
|
||||||
|
} catch (e, s) {
|
||||||
|
logssaved = false;
|
||||||
|
Logging.instance
|
||||||
|
.log("$e $s", level: LogLevel.Error);
|
||||||
|
}
|
||||||
|
|
||||||
shouldPop = true;
|
shouldPop = true;
|
||||||
|
|
||||||
|
@ -350,7 +426,9 @@ class _DebugViewState extends ConsumerState<DebugView> {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => StackOkDialog(
|
builder: (context) => StackOkDialog(
|
||||||
title: "Logs saved to",
|
title: logssaved
|
||||||
|
? "Logs saved to"
|
||||||
|
: "Error Saving Logs",
|
||||||
message: "${path!}/$filename",
|
message: "${path!}/$filename",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -360,7 +438,9 @@ class _DebugViewState extends ConsumerState<DebugView> {
|
||||||
showFloatingFlushBar(
|
showFloatingFlushBar(
|
||||||
type: FlushBarType.info,
|
type: FlushBarType.info,
|
||||||
context: context,
|
context: context,
|
||||||
message: 'Logs file saved',
|
message: logssaved
|
||||||
|
? 'Logs file saved'
|
||||||
|
: "Error Saving Logs",
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -687,27 +687,14 @@ abstract class SWB {
|
||||||
uiState?.walletStates = walletStates;
|
uiState?.walletStates = walletStates;
|
||||||
|
|
||||||
List<Future<bool>> restoreStatuses = [];
|
List<Future<bool>> restoreStatuses = [];
|
||||||
final List<Tuple2<dynamic, Manager>> firoWallets = [];
|
|
||||||
final List<Tuple2<dynamic, Manager>> firoTestnetWallets = [];
|
|
||||||
final List<Tuple2<dynamic, Manager>> epicCashWallets = [];
|
|
||||||
// start restoring wallets
|
// start restoring wallets
|
||||||
for (final tuple in managers) {
|
for (final tuple in managers) {
|
||||||
// check if cancel was requested and restore previous state
|
// check if cancel was requested and restore previous state
|
||||||
if (_checkShouldCancel(preRestoreState)) {
|
if (_checkShouldCancel(preRestoreState)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
final bools = await asyncRestore(tuple, uiState, walletsService);
|
||||||
if (tuple.item2.coin == Coin.firoTestNet) {
|
restoreStatuses.add(Future(() => bools));
|
||||||
firoTestnetWallets.add(tuple);
|
|
||||||
continue;
|
|
||||||
} else if (tuple.item2.coin == Coin.firo) {
|
|
||||||
firoWallets.add(tuple);
|
|
||||||
continue;
|
|
||||||
} else if (tuple.item2.coin == Coin.epicCash) {
|
|
||||||
epicCashWallets.add(tuple);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
restoreStatuses.add(asyncRestore(tuple, uiState, walletsService));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if cancel was requested and restore previous state
|
// check if cancel was requested and restore previous state
|
||||||
|
@ -715,153 +702,6 @@ abstract class SWB {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (firoTestnetWallets.isNotEmpty) {
|
|
||||||
// check if cancel was requested and restore previous state
|
|
||||||
if (_checkShouldCancel(preRestoreState)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (final wallet in firoTestnetWallets) {
|
|
||||||
uiState?.update(
|
|
||||||
walletId: wallet.item2.walletId,
|
|
||||||
restoringStatus: StackRestoringStatus.restoring,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// try using node from backup first
|
|
||||||
NodeModel node = nodeService.getPrimaryNodeFor(coin: Coin.firoTestNet) ??
|
|
||||||
DefaultNodes.getNodeFor(Coin.firoTestNet);
|
|
||||||
|
|
||||||
final electrumxNode = ElectrumXNode(
|
|
||||||
address: node.host,
|
|
||||||
port: node.port,
|
|
||||||
name: node.name,
|
|
||||||
id: node.id,
|
|
||||||
useSSL: node.useSSL,
|
|
||||||
);
|
|
||||||
|
|
||||||
final failovers = nodeService.failoverNodesFor(coin: Coin.firoTestNet);
|
|
||||||
|
|
||||||
// check if cancel was requested and restore previous state
|
|
||||||
if (_checkShouldCancel(preRestoreState)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final cachedClient = CachedElectrumX.from(
|
|
||||||
node: electrumxNode,
|
|
||||||
prefs: _prefs,
|
|
||||||
failovers: failovers
|
|
||||||
.map(
|
|
||||||
(e) => ElectrumXNode(
|
|
||||||
address: e.host,
|
|
||||||
port: e.port,
|
|
||||||
name: e.name,
|
|
||||||
id: e.id,
|
|
||||||
useSSL: e.useSSL,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.toList(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// check if cancel was requested and restore previous state
|
|
||||||
if (_checkShouldCancel(preRestoreState)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Anonymity Set often fails when gathering from the server
|
|
||||||
const int maxTries = 5;
|
|
||||||
for (int j = 0; j < maxTries; j++) {
|
|
||||||
// check if cancel was requested and restore previous state
|
|
||||||
if (_checkShouldCancel(preRestoreState)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await cachedClient.getAnonymitySet(
|
|
||||||
groupId: "1",
|
|
||||||
coin: Coin.firoTestNet,
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
} catch (_) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// check if cancel was requested and restore previous state
|
|
||||||
if (_checkShouldCancel(preRestoreState)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (firoWallets.isNotEmpty) {
|
|
||||||
for (final wallet in firoWallets) {
|
|
||||||
uiState?.update(
|
|
||||||
walletId: wallet.item2.walletId,
|
|
||||||
restoringStatus: StackRestoringStatus.restoring,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if cancel was requested and restore previous state
|
|
||||||
if (_checkShouldCancel(preRestoreState)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// try using node from backup first
|
|
||||||
NodeModel node = nodeService.getPrimaryNodeFor(coin: Coin.firo) ??
|
|
||||||
DefaultNodes.getNodeFor(Coin.firo);
|
|
||||||
|
|
||||||
final electrumxNode = ElectrumXNode(
|
|
||||||
address: node.host,
|
|
||||||
port: node.port,
|
|
||||||
name: node.name,
|
|
||||||
id: node.id,
|
|
||||||
useSSL: node.useSSL,
|
|
||||||
);
|
|
||||||
final failovers = nodeService.failoverNodesFor(coin: Coin.firoTestNet);
|
|
||||||
final cachedClient = CachedElectrumX.from(
|
|
||||||
node: electrumxNode,
|
|
||||||
prefs: _prefs,
|
|
||||||
failovers: failovers
|
|
||||||
.map(
|
|
||||||
(e) => ElectrumXNode(
|
|
||||||
address: e.host,
|
|
||||||
port: e.port,
|
|
||||||
name: e.name,
|
|
||||||
id: e.id,
|
|
||||||
useSSL: e.useSSL,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.toList());
|
|
||||||
|
|
||||||
// Anonymity Set often fails when gathering from the server
|
|
||||||
const int maxTries = 5;
|
|
||||||
for (int j = 0; j < maxTries; j++) {
|
|
||||||
// check if cancel was requested and restore previous state
|
|
||||||
if (_checkShouldCancel(preRestoreState)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
await cachedClient.getAnonymitySet(
|
|
||||||
groupId: "1",
|
|
||||||
coin: Coin.firo,
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
} catch (_) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if cancel was requested and restore previous state
|
|
||||||
if (_checkShouldCancel(preRestoreState)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (final tuple in firoTestnetWallets) {
|
|
||||||
restoreStatuses.add(asyncRestore(tuple, uiState, walletsService));
|
|
||||||
}
|
|
||||||
for (final tuple in firoWallets) {
|
|
||||||
restoreStatuses.add(asyncRestore(tuple, uiState, walletsService));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Future<bool> status in restoreStatuses) {
|
for (Future<bool> status in restoreStatuses) {
|
||||||
// check if cancel was requested and restore previous state
|
// check if cancel was requested and restore previous state
|
||||||
if (_checkShouldCancel(preRestoreState)) {
|
if (_checkShouldCancel(preRestoreState)) {
|
||||||
|
@ -869,13 +709,7 @@ abstract class SWB {
|
||||||
}
|
}
|
||||||
await status;
|
await status;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < epicCashWallets.length; i++) {
|
|
||||||
// check if cancel was requested and restore previous state
|
|
||||||
if (_checkShouldCancel(preRestoreState)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
await asyncRestore(epicCashWallets[i], uiState, walletsService);
|
|
||||||
}
|
|
||||||
if (!Platform.isLinux) await Wakelock.disable();
|
if (!Platform.isLinux) await Wakelock.disable();
|
||||||
// check if cancel was requested and restore previous state
|
// check if cancel was requested and restore previous state
|
||||||
if (_checkShouldCancel(preRestoreState)) {
|
if (_checkShouldCancel(preRestoreState)) {
|
||||||
|
|
|
@ -445,7 +445,7 @@ class _StackRestoreProgressViewState
|
||||||
},
|
},
|
||||||
style: Theme.of(context)
|
style: Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
.getSecondaryEnabledButtonColor(context),
|
.getPrimaryEnabledButtonColor(context),
|
||||||
child: Text(
|
child: Text(
|
||||||
_success ? "OK" : "Cancel restore process",
|
_success ? "OK" : "Cancel restore process",
|
||||||
style: STextStyles.button(context).copyWith(
|
style: STextStyles.button(context).copyWith(
|
||||||
|
|
|
@ -43,7 +43,7 @@ import 'package:stackwallet/utilities/prefs.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
const int MINIMUM_CONFIRMATIONS = 3;
|
const int MINIMUM_CONFIRMATIONS = 1;
|
||||||
const int DUST_LIMIT = 546;
|
const int DUST_LIMIT = 546;
|
||||||
|
|
||||||
const String GENESIS_HASH_MAINNET =
|
const String GENESIS_HASH_MAINNET =
|
||||||
|
@ -265,6 +265,11 @@ class BitcoinCashWallet extends CoinServiceAPI {
|
||||||
DerivePathType addressType({required String address}) {
|
DerivePathType addressType({required String address}) {
|
||||||
Uint8List? decodeBase58;
|
Uint8List? decodeBase58;
|
||||||
Segwit? decodeBech32;
|
Segwit? decodeBech32;
|
||||||
|
try {
|
||||||
|
if (Bitbox.Address.detectFormat(address) == 0) {
|
||||||
|
address = Bitbox.Address.toLegacyAddress(address);
|
||||||
|
}
|
||||||
|
} catch (e, s) {}
|
||||||
try {
|
try {
|
||||||
decodeBase58 = bs58check.decode(address);
|
decodeBase58 = bs58check.decode(address);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -825,9 +830,6 @@ class BitcoinCashWallet extends CoinServiceAPI {
|
||||||
/// Refreshes display data for the wallet
|
/// Refreshes display data for the wallet
|
||||||
@override
|
@override
|
||||||
Future<void> refresh() async {
|
Future<void> refresh() async {
|
||||||
final bchaddr = Bitbox.Address.toCashAddress(await currentReceivingAddress);
|
|
||||||
print("bchaddr: $bchaddr ${await currentReceivingAddress}");
|
|
||||||
|
|
||||||
if (refreshMutex) {
|
if (refreshMutex) {
|
||||||
Logging.instance.log("$walletId $walletName refreshMutex denied",
|
Logging.instance.log("$walletId $walletName refreshMutex denied",
|
||||||
level: LogLevel.Info);
|
level: LogLevel.Info);
|
||||||
|
@ -1384,7 +1386,9 @@ class BitcoinCashWallet extends CoinServiceAPI {
|
||||||
initialChangeAddressP2SH, 1, DerivePathType.bip49);
|
initialChangeAddressP2SH, 1, DerivePathType.bip49);
|
||||||
|
|
||||||
// this._currentReceivingAddress = Future(() => initialReceivingAddress);
|
// this._currentReceivingAddress = Future(() => initialReceivingAddress);
|
||||||
_currentReceivingAddressP2PKH = Future(() => initialReceivingAddressP2PKH);
|
|
||||||
|
var newaddr = await _getCurrentAddressForChain(0, DerivePathType.bip44);
|
||||||
|
_currentReceivingAddressP2PKH = Future(() => newaddr);
|
||||||
_currentReceivingAddressP2SH = Future(() => initialReceivingAddressP2SH);
|
_currentReceivingAddressP2SH = Future(() => initialReceivingAddressP2SH);
|
||||||
|
|
||||||
Logging.instance.log("_generateNewWalletFinished", level: LogLevel.Info);
|
Logging.instance.log("_generateNewWalletFinished", level: LogLevel.Info);
|
||||||
|
@ -1521,6 +1525,11 @@ class BitcoinCashWallet extends CoinServiceAPI {
|
||||||
print("Array key is ${jsonEncode(arrayKey)}");
|
print("Array key is ${jsonEncode(arrayKey)}");
|
||||||
final internalChainArray =
|
final internalChainArray =
|
||||||
DB.instance.get<dynamic>(boxName: walletId, key: arrayKey);
|
DB.instance.get<dynamic>(boxName: walletId, key: arrayKey);
|
||||||
|
if (derivePathType == DerivePathType.bip44) {
|
||||||
|
if (Bitbox.Address.detectFormat(internalChainArray.last as String) == 1) {
|
||||||
|
return Bitbox.Address.toCashAddress(internalChainArray.last as String);
|
||||||
|
}
|
||||||
|
}
|
||||||
return internalChainArray.last as String;
|
return internalChainArray.last as String;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1986,6 +1995,9 @@ class BitcoinCashWallet extends CoinServiceAPI {
|
||||||
/// Returns the scripthash or throws an exception on invalid bch address
|
/// Returns the scripthash or throws an exception on invalid bch address
|
||||||
String _convertToScriptHash(String bchAddress, NetworkType network) {
|
String _convertToScriptHash(String bchAddress, NetworkType network) {
|
||||||
try {
|
try {
|
||||||
|
if (Bitbox.Address.detectFormat(bchAddress) == 0) {
|
||||||
|
bchAddress = Bitbox.Address.toLegacyAddress(bchAddress);
|
||||||
|
}
|
||||||
final output = Address.addressToOutputScript(bchAddress, network);
|
final output = Address.addressToOutputScript(bchAddress, network);
|
||||||
final hash = sha256.convert(output.toList(growable: false)).toString();
|
final hash = sha256.convert(output.toList(growable: false)).toString();
|
||||||
|
|
||||||
|
@ -2058,11 +2070,27 @@ class BitcoinCashWallet extends CoinServiceAPI {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<TransactionData> _fetchTransactionData() async {
|
Future<TransactionData> _fetchTransactionData() async {
|
||||||
final List<String> allAddresses = await _fetchAllOwnAddresses();
|
List<String> allAddressesOld = await _fetchAllOwnAddresses();
|
||||||
|
List<String> allAddresses = [];
|
||||||
|
for (String address in allAddressesOld) {
|
||||||
|
if (Bitbox.Address.detectFormat(address) == 1) {
|
||||||
|
allAddresses.add(Bitbox.Address.toCashAddress(address));
|
||||||
|
} else {
|
||||||
|
allAddresses.add(address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final changeAddressesP2PKH =
|
var changeAddressesP2PKHOld =
|
||||||
DB.instance.get<dynamic>(boxName: walletId, key: 'changeAddressesP2PKH')
|
DB.instance.get<dynamic>(boxName: walletId, key: 'changeAddressesP2PKH')
|
||||||
as List<dynamic>;
|
as List<dynamic>;
|
||||||
|
List<dynamic> changeAddressesP2PKH = [];
|
||||||
|
for (var address in changeAddressesP2PKHOld) {
|
||||||
|
if (Bitbox.Address.detectFormat(address as String) == 1) {
|
||||||
|
changeAddressesP2PKH.add(Bitbox.Address.toCashAddress(address));
|
||||||
|
} else {
|
||||||
|
changeAddressesP2PKH.add(address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final List<Map<String, dynamic>> allTxHashes =
|
final List<Map<String, dynamic>> allTxHashes =
|
||||||
await _fetchHistory(allAddresses);
|
await _fetchHistory(allAddresses);
|
||||||
|
@ -2087,7 +2115,16 @@ class BitcoinCashWallet extends CoinServiceAPI {
|
||||||
if (txHeight > 0 &&
|
if (txHeight > 0 &&
|
||||||
txHeight < latestTxnBlockHeight - MINIMUM_CONFIRMATIONS) {
|
txHeight < latestTxnBlockHeight - MINIMUM_CONFIRMATIONS) {
|
||||||
if (unconfirmedCachedTransactions[tx["tx_hash"] as String] == null) {
|
if (unconfirmedCachedTransactions[tx["tx_hash"] as String] == null) {
|
||||||
allTxHashes.remove(tx);
|
print(cachedTransactions.findTransaction(tx["tx_hash"] as String));
|
||||||
|
print(unconfirmedCachedTransactions[tx["tx_hash"] as String]);
|
||||||
|
final cachedTx =
|
||||||
|
cachedTransactions.findTransaction(tx["tx_hash"] as String);
|
||||||
|
if (!(cachedTx != null &&
|
||||||
|
addressType(address: cachedTx.address) ==
|
||||||
|
DerivePathType.bip44 &&
|
||||||
|
Bitbox.Address.detectFormat(cachedTx.address) == 1)) {
|
||||||
|
allTxHashes.remove(tx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2096,7 +2133,6 @@ class BitcoinCashWallet extends CoinServiceAPI {
|
||||||
List<Map<String, dynamic>> allTransactions = [];
|
List<Map<String, dynamic>> allTransactions = [];
|
||||||
|
|
||||||
for (final txHash in allTxHashes) {
|
for (final txHash in allTxHashes) {
|
||||||
Logging.instance.log("bch: $txHash", level: LogLevel.Info);
|
|
||||||
final tx = await cachedElectrumXClient.getTransaction(
|
final tx = await cachedElectrumXClient.getTransaction(
|
||||||
txHash: txHash["tx_hash"] as String,
|
txHash: txHash["tx_hash"] as String,
|
||||||
verbose: true,
|
verbose: true,
|
||||||
|
@ -2166,7 +2202,8 @@ class BitcoinCashWallet extends CoinServiceAPI {
|
||||||
.log("recipientsArray: $recipientsArray", level: LogLevel.Info);
|
.log("recipientsArray: $recipientsArray", level: LogLevel.Info);
|
||||||
|
|
||||||
final foundInSenders =
|
final foundInSenders =
|
||||||
allAddresses.any((element) => sendersArray.contains(element));
|
allAddresses.any((element) => sendersArray.contains(element)) ||
|
||||||
|
allAddressesOld.any((element) => sendersArray.contains(element));
|
||||||
Logging.instance
|
Logging.instance
|
||||||
.log("foundInSenders: $foundInSenders", level: LogLevel.Info);
|
.log("foundInSenders: $foundInSenders", level: LogLevel.Info);
|
||||||
|
|
||||||
|
@ -2228,7 +2265,8 @@ class BitcoinCashWallet extends CoinServiceAPI {
|
||||||
.toBigInt()
|
.toBigInt()
|
||||||
.toInt();
|
.toInt();
|
||||||
totalOut += value;
|
totalOut += value;
|
||||||
if (allAddresses.contains(address)) {
|
if (allAddresses.contains(address) ||
|
||||||
|
allAddressesOld.contains(address)) {
|
||||||
outputAmtAddressedToWallet += value;
|
outputAmtAddressedToWallet += value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2743,7 +2781,10 @@ class BitcoinCashWallet extends CoinServiceAPI {
|
||||||
for (final output in tx["vout"] as List) {
|
for (final output in tx["vout"] as List) {
|
||||||
final n = output["n"];
|
final n = output["n"];
|
||||||
if (n != null && n == utxosToUse[i].vout) {
|
if (n != null && n == utxosToUse[i].vout) {
|
||||||
final address = output["scriptPubKey"]["addresses"][0] as String;
|
String address = output["scriptPubKey"]["addresses"][0] as String;
|
||||||
|
if (Bitbox.Address.detectFormat(address) == 0) {
|
||||||
|
address = Bitbox.Address.toLegacyAddress(address);
|
||||||
|
}
|
||||||
if (!addressTxid.containsKey(address)) {
|
if (!addressTxid.containsKey(address)) {
|
||||||
addressTxid[address] = <String>[];
|
addressTxid[address] = <String>[];
|
||||||
}
|
}
|
||||||
|
@ -2772,8 +2813,13 @@ class BitcoinCashWallet extends CoinServiceAPI {
|
||||||
derivePathType: DerivePathType.bip44,
|
derivePathType: DerivePathType.bip44,
|
||||||
);
|
);
|
||||||
for (int i = 0; i < p2pkhLength; i++) {
|
for (int i = 0; i < p2pkhLength; i++) {
|
||||||
|
String address = addressesP2PKH[i];
|
||||||
|
if (Bitbox.Address.detectFormat(address) == 0) {
|
||||||
|
address = Bitbox.Address.toLegacyAddress(address);
|
||||||
|
}
|
||||||
|
|
||||||
// receives
|
// receives
|
||||||
final receiveDerivation = receiveDerivations[addressesP2PKH[i]];
|
final receiveDerivation = receiveDerivations[address];
|
||||||
// if a match exists it will not be null
|
// if a match exists it will not be null
|
||||||
if (receiveDerivation != null) {
|
if (receiveDerivation != null) {
|
||||||
final data = P2PKH(
|
final data = P2PKH(
|
||||||
|
@ -2783,7 +2829,7 @@ class BitcoinCashWallet extends CoinServiceAPI {
|
||||||
network: _network,
|
network: _network,
|
||||||
).data;
|
).data;
|
||||||
|
|
||||||
for (String tx in addressTxid[addressesP2PKH[i]]!) {
|
for (String tx in addressTxid[address]!) {
|
||||||
results[tx] = {
|
results[tx] = {
|
||||||
"output": data.output,
|
"output": data.output,
|
||||||
"keyPair": ECPair.fromWIF(
|
"keyPair": ECPair.fromWIF(
|
||||||
|
@ -2794,7 +2840,7 @@ class BitcoinCashWallet extends CoinServiceAPI {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// if its not a receive, check change
|
// if its not a receive, check change
|
||||||
final changeDerivation = changeDerivations[addressesP2PKH[i]];
|
final changeDerivation = changeDerivations[address];
|
||||||
// if a match exists it will not be null
|
// if a match exists it will not be null
|
||||||
if (changeDerivation != null) {
|
if (changeDerivation != null) {
|
||||||
final data = P2PKH(
|
final data = P2PKH(
|
||||||
|
@ -2804,7 +2850,7 @@ class BitcoinCashWallet extends CoinServiceAPI {
|
||||||
network: _network,
|
network: _network,
|
||||||
).data;
|
).data;
|
||||||
|
|
||||||
for (String tx in addressTxid[addressesP2PKH[i]]!) {
|
for (String tx in addressTxid[address]!) {
|
||||||
results[tx] = {
|
results[tx] = {
|
||||||
"output": data.output,
|
"output": data.output,
|
||||||
"keyPair": ECPair.fromWIF(
|
"keyPair": ECPair.fromWIF(
|
||||||
|
@ -3377,8 +3423,9 @@ class BitcoinCashWallet extends CoinServiceAPI {
|
||||||
0,
|
0,
|
||||||
DerivePathType
|
DerivePathType
|
||||||
.bip44); // Add that new receiving address to the array of receiving addresses
|
.bip44); // Add that new receiving address to the array of receiving addresses
|
||||||
_currentReceivingAddressP2PKH = Future(() =>
|
var newaddr = await _getCurrentAddressForChain(0, DerivePathType.bip44);
|
||||||
newReceivingAddress); // Set the new receiving address that the service
|
_currentReceivingAddressP2PKH = Future(
|
||||||
|
() => newaddr); // Set the new receiving address that the service
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
|
|
|
@ -1990,7 +1990,6 @@ class EpicCashWallet extends CoinServiceAPI {
|
||||||
|
|
||||||
Future<bool> refreshIfThereIsNewData() async {
|
Future<bool> refreshIfThereIsNewData() async {
|
||||||
if (_hasCalledExit) return false;
|
if (_hasCalledExit) return false;
|
||||||
Logging.instance.log("Can we do this here?", level: LogLevel.Fatal);
|
|
||||||
// TODO returning true here signals this class to call refresh() after which it will fire an event that notifies the UI that new data has been fetched/found for this wallet
|
// TODO returning true here signals this class to call refresh() after which it will fire an event that notifies the UI that new data has been fetched/found for this wallet
|
||||||
return true;
|
return true;
|
||||||
// TODO: do a quick check to see if there is any new data that would require a refresh
|
// TODO: do a quick check to see if there is any new data that would require a refresh
|
||||||
|
|
|
@ -39,6 +39,19 @@ class NodeService extends ChangeNotifier {
|
||||||
key: savedNode.id,
|
key: savedNode.id,
|
||||||
value: defaultNode.copyWith(enabled: savedNode.enabled));
|
value: defaultNode.copyWith(enabled: savedNode.enabled));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if a default node is the primary node for the crypto currency
|
||||||
|
// and update it if needed
|
||||||
|
final coin = coinFromPrettyName(defaultNode.coinName);
|
||||||
|
final primaryNode = getPrimaryNodeFor(coin: coin);
|
||||||
|
if (primaryNode != null && primaryNode.id == defaultNode.id) {
|
||||||
|
await setPrimaryNodeFor(
|
||||||
|
coin: coin,
|
||||||
|
node: defaultNode.copyWith(
|
||||||
|
enabled: primaryNode.enabled,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,12 +78,12 @@ class PriceAPI {
|
||||||
}
|
}
|
||||||
|
|
||||||
final externalCalls = Prefs.instance.externalCalls;
|
final externalCalls = Prefs.instance.externalCalls;
|
||||||
if (!Logger.isTestEnv && !externalCalls) {
|
if ((!Logger.isTestEnv && !externalCalls) ||
|
||||||
|
!(await Prefs.instance.isExternalCallsSet())) {
|
||||||
Logging.instance.log("User does not want to use external calls",
|
Logging.instance.log("User does not want to use external calls",
|
||||||
level: LogLevel.Info);
|
level: LogLevel.Info);
|
||||||
return _cachedPrices;
|
return _cachedPrices;
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<Coin, Tuple2<Decimal, double>> result = {};
|
Map<Coin, Tuple2<Decimal, double>> result = {};
|
||||||
try {
|
try {
|
||||||
final uri = Uri.parse(
|
final uri = Uri.parse(
|
||||||
|
@ -123,7 +123,8 @@ class PriceAPI {
|
||||||
|
|
||||||
static Future<List<String>?> availableBaseCurrencies() async {
|
static Future<List<String>?> availableBaseCurrencies() async {
|
||||||
final externalCalls = Prefs.instance.externalCalls;
|
final externalCalls = Prefs.instance.externalCalls;
|
||||||
if (!Logger.isTestEnv && !externalCalls) {
|
if ((!Logger.isTestEnv && !externalCalls) ||
|
||||||
|
!(await Prefs.instance.isExternalCallsSet())) {
|
||||||
Logging.instance.log("User does not want to use external calls",
|
Logging.instance.log("User does not want to use external calls",
|
||||||
level: LogLevel.Info);
|
level: LogLevel.Info);
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
|
|
||||||
class _LayoutSizing {
|
class _LayoutSizing {
|
||||||
const _LayoutSizing();
|
const _LayoutSizing();
|
||||||
|
@ -14,7 +15,12 @@ class _LayoutSizing {
|
||||||
abstract class Constants {
|
abstract class Constants {
|
||||||
static const size = _LayoutSizing();
|
static const size = _LayoutSizing();
|
||||||
|
|
||||||
static final bool enableExchange = !Platform.isIOS;
|
static void exchangeForExperiencedUsers(int count) {
|
||||||
|
enableExchange =
|
||||||
|
Util.isDesktop || Platform.isAndroid || count > 5 || !Platform.isIOS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool enableExchange = Util.isDesktop || !Platform.isIOS;
|
||||||
|
|
||||||
//TODO: correct for monero?
|
//TODO: correct for monero?
|
||||||
static const int satsPerCoinMonero = 1000000000000;
|
static const int satsPerCoinMonero = 1000000000000;
|
||||||
|
@ -36,7 +42,7 @@ abstract class Constants {
|
||||||
// Enable Logger.print statements
|
// Enable Logger.print statements
|
||||||
static const bool disableLogger = false;
|
static const bool disableLogger = false;
|
||||||
|
|
||||||
static const int currentHiveDbVersion = 2;
|
static const int currentHiveDbVersion = 3;
|
||||||
|
|
||||||
static List<int> possibleLengthsForCoin(Coin coin) {
|
static List<int> possibleLengthsForCoin(Coin coin) {
|
||||||
final List<int> values = [];
|
final List<int> values = [];
|
||||||
|
|
|
@ -143,6 +143,18 @@ class DbVersionMigrator {
|
||||||
|
|
||||||
// try to continue migrating
|
// try to continue migrating
|
||||||
return await migrate(2);
|
return await migrate(2);
|
||||||
|
case 2:
|
||||||
|
await Hive.openBox<dynamic>(DB.boxNamePrefs);
|
||||||
|
final prefs = Prefs.instance;
|
||||||
|
await prefs.init();
|
||||||
|
if (!(await prefs.isExternalCallsSet())) {
|
||||||
|
prefs.externalCalls = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update version
|
||||||
|
await DB.instance.put<dynamic>(
|
||||||
|
boxName: DB.boxNameDBInfo, key: "hive_data_version", value: 3);
|
||||||
|
return await migrate(3);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// finally return
|
// finally return
|
||||||
|
|
|
@ -70,28 +70,24 @@ abstract class DefaultNodes {
|
||||||
isDown: false,
|
isDown: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO: eventually enable ssl and set scheme to https
|
|
||||||
// currently get certificate failure
|
|
||||||
static NodeModel get monero => NodeModel(
|
static NodeModel get monero => NodeModel(
|
||||||
host: "http://monero.stackwallet.com",
|
host: "https://monero.stackwallet.com",
|
||||||
port: 18081,
|
port: 18081,
|
||||||
name: defaultName,
|
name: defaultName,
|
||||||
id: _nodeId(Coin.monero),
|
id: _nodeId(Coin.monero),
|
||||||
useSSL: false,
|
useSSL: true,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
coinName: Coin.monero.name,
|
coinName: Coin.monero.name,
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
isDown: false,
|
isDown: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO: eventually enable ssl and set scheme to https
|
|
||||||
// currently get certificate failure
|
|
||||||
static NodeModel get wownero => NodeModel(
|
static NodeModel get wownero => NodeModel(
|
||||||
host: "http://eu-west-2.wow.xmr.pm",
|
host: "https://wownero.stackwallet.com",
|
||||||
port: 34568,
|
port: 34568,
|
||||||
name: defaultName,
|
name: defaultName,
|
||||||
id: _nodeId(Coin.wownero),
|
id: _nodeId(Coin.wownero),
|
||||||
useSSL: false,
|
useSSL: true,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
coinName: Coin.wownero.name,
|
coinName: Coin.wownero.name,
|
||||||
isFailover: true,
|
isFailover: true,
|
||||||
|
|
|
@ -181,25 +181,32 @@ Coin coinFromPrettyName(String name) {
|
||||||
case "Bitcoin":
|
case "Bitcoin":
|
||||||
case "bitcoin":
|
case "bitcoin":
|
||||||
return Coin.bitcoin;
|
return Coin.bitcoin;
|
||||||
|
|
||||||
case "Bitcoincash":
|
case "Bitcoincash":
|
||||||
case "bitcoincash":
|
case "bitcoincash":
|
||||||
case "Bitcoin Cash":
|
case "Bitcoin Cash":
|
||||||
return Coin.bitcoincash;
|
return Coin.bitcoincash;
|
||||||
|
|
||||||
case "Dogecoin":
|
case "Dogecoin":
|
||||||
case "dogecoin":
|
case "dogecoin":
|
||||||
return Coin.dogecoin;
|
return Coin.dogecoin;
|
||||||
|
|
||||||
case "Epic Cash":
|
case "Epic Cash":
|
||||||
case "epicCash":
|
case "epicCash":
|
||||||
return Coin.epicCash;
|
return Coin.epicCash;
|
||||||
|
|
||||||
case "Firo":
|
case "Firo":
|
||||||
case "firo":
|
case "firo":
|
||||||
return Coin.firo;
|
return Coin.firo;
|
||||||
|
|
||||||
case "Monero":
|
case "Monero":
|
||||||
case "monero":
|
case "monero":
|
||||||
return Coin.monero;
|
return Coin.monero;
|
||||||
|
|
||||||
case "Namecoin":
|
case "Namecoin":
|
||||||
case "namecoin":
|
case "namecoin":
|
||||||
return Coin.namecoin;
|
return Coin.namecoin;
|
||||||
|
|
||||||
case "Bitcoin Testnet":
|
case "Bitcoin Testnet":
|
||||||
case "tBitcoin":
|
case "tBitcoin":
|
||||||
case "bitcoinTestNet":
|
case "bitcoinTestNet":
|
||||||
|
@ -208,19 +215,24 @@ Coin coinFromPrettyName(String name) {
|
||||||
case "Bitcoincash Testnet":
|
case "Bitcoincash Testnet":
|
||||||
case "tBitcoin Cash":
|
case "tBitcoin Cash":
|
||||||
case "Bitcoin Cash Testnet":
|
case "Bitcoin Cash Testnet":
|
||||||
|
case "bitcoincashTestnet":
|
||||||
return Coin.bitcoincashTestnet;
|
return Coin.bitcoincashTestnet;
|
||||||
|
|
||||||
case "Firo Testnet":
|
case "Firo Testnet":
|
||||||
case "tFiro":
|
case "tFiro":
|
||||||
case "firoTestNet":
|
case "firoTestNet":
|
||||||
return Coin.firoTestNet;
|
return Coin.firoTestNet;
|
||||||
|
|
||||||
case "Dogecoin Testnet":
|
case "Dogecoin Testnet":
|
||||||
case "tDogecoin":
|
case "tDogecoin":
|
||||||
case "dogecoinTestNet":
|
case "dogecoinTestNet":
|
||||||
return Coin.dogecoinTestNet;
|
return Coin.dogecoinTestNet;
|
||||||
|
|
||||||
case "Wownero":
|
case "Wownero":
|
||||||
case "tWownero":
|
case "tWownero":
|
||||||
case "wownero":
|
case "wownero":
|
||||||
return Coin.wownero;
|
return Coin.wownero;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw ArgumentError.value(
|
throw ArgumentError.value(
|
||||||
name, "name", "No Coin enum value with that prettyName");
|
name, "name", "No Coin enum value with that prettyName");
|
||||||
|
|
|
@ -571,4 +571,13 @@ class Prefs extends ChangeNotifier {
|
||||||
boxName: DB.boxNamePrefs, key: "externalCalls") as bool? ??
|
boxName: DB.boxNamePrefs, key: "externalCalls") as bool? ??
|
||||||
true;
|
true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> isExternalCallsSet() async {
|
||||||
|
if (await DB.instance
|
||||||
|
.get<dynamic>(boxName: DB.boxNamePrefs, key: "externalCalls") ==
|
||||||
|
null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -306,10 +306,10 @@ class NodeOptionsSheet extends ConsumerWidget {
|
||||||
style: status == "Connected"
|
style: status == "Connected"
|
||||||
? Theme.of(context)
|
? Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
.getPrimaryEnabledButtonColor(context)
|
.getPrimaryDisabledButtonColor(context)
|
||||||
: Theme.of(context)
|
: Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
.getPrimaryDisabledButtonColor(context),
|
.getPrimaryEnabledButtonColor(context),
|
||||||
onPressed: status == "Connected"
|
onPressed: status == "Connected"
|
||||||
? null
|
? null
|
||||||
: () async {
|
: () async {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import FlutterMacOS
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
import connectivity_plus_macos
|
import connectivity_plus_macos
|
||||||
|
import device_info_plus
|
||||||
import devicelocale
|
import devicelocale
|
||||||
import flutter_libepiccash
|
import flutter_libepiccash
|
||||||
import flutter_local_notifications
|
import flutter_local_notifications
|
||||||
|
@ -22,6 +23,7 @@ import window_size
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin"))
|
ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin"))
|
||||||
|
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||||
DevicelocalePlugin.register(with: registry.registrar(forPlugin: "DevicelocalePlugin"))
|
DevicelocalePlugin.register(with: registry.registrar(forPlugin: "DevicelocalePlugin"))
|
||||||
FlutterLibepiccashPlugin.register(with: registry.registrar(forPlugin: "FlutterLibepiccashPlugin"))
|
FlutterLibepiccashPlugin.register(with: registry.registrar(forPlugin: "FlutterLibepiccashPlugin"))
|
||||||
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
|
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
|
||||||
|
|
23
pubspec.lock
23
pubspec.lock
|
@ -380,6 +380,20 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.2"
|
version: "3.2.2"
|
||||||
|
device_info_plus:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: device_info_plus
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "7.0.1"
|
||||||
|
device_info_plus_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: device_info_plus_platform_interface
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "6.0.1"
|
||||||
devicelocale:
|
devicelocale:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -1396,7 +1410,14 @@ packages:
|
||||||
name: string_scanner
|
name: string_scanner
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.1"
|
||||||
|
string_validator:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: string_validator
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.3.0"
|
||||||
sync_http:
|
sync_http:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -11,7 +11,7 @@ description: Stack Wallet
|
||||||
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
||||||
# Read more about iOS versioning at
|
# Read more about iOS versioning at
|
||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
version: 1.5.8+78
|
version: 1.5.9+79
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.17.0 <3.0.0"
|
sdk: ">=2.17.0 <3.0.0"
|
||||||
|
@ -115,6 +115,7 @@ dependencies:
|
||||||
wakelock: ^0.6.2
|
wakelock: ^0.6.2
|
||||||
intl: ^0.17.0
|
intl: ^0.17.0
|
||||||
devicelocale: ^0.5.0
|
devicelocale: ^0.5.0
|
||||||
|
device_info_plus: ^7.0.1
|
||||||
keyboard_dismisser: ^3.0.0
|
keyboard_dismisser: ^3.0.0
|
||||||
another_flushbar: ^1.10.28
|
another_flushbar: ^1.10.28
|
||||||
tuple: ^2.0.0
|
tuple: ^2.0.0
|
||||||
|
@ -132,6 +133,7 @@ dependencies:
|
||||||
isar: 3.0.0-dev.10
|
isar: 3.0.0-dev.10
|
||||||
isar_flutter_libs: 3.0.0-dev.10 # contains the binaries
|
isar_flutter_libs: 3.0.0-dev.10 # contains the binaries
|
||||||
dropdown_button2: 1.7.2
|
dropdown_button2: 1.7.2
|
||||||
|
string_validator: ^0.3.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
47
test/notifications/notification_card_test.dart
Normal file
47
test/notifications/notification_card_test.dart
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:stackwallet/models/notification_model.dart';
|
||||||
|
import 'package:stackwallet/notifications/notification_card.dart';
|
||||||
|
import 'package:stackwallet/utilities/assets.dart';
|
||||||
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
|
import 'package:stackwallet/utilities/theme/light_colors.dart';
|
||||||
|
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets("test notification card", (widgetTester) async {
|
||||||
|
final key = UniqueKey();
|
||||||
|
final notificationCard = NotificationCard(
|
||||||
|
key: key,
|
||||||
|
notification: NotificationModel(
|
||||||
|
id: 1,
|
||||||
|
title: "notification title",
|
||||||
|
description: "notification description",
|
||||||
|
iconAssetName: Assets.svg.iconFor(coin: Coin.bitcoin),
|
||||||
|
date: DateTime.parse("1662544771"),
|
||||||
|
walletId: "wallet id",
|
||||||
|
read: true,
|
||||||
|
shouldWatchForUpdates: true,
|
||||||
|
coinName: "Bitcoin"),
|
||||||
|
);
|
||||||
|
|
||||||
|
await widgetTester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: ThemeData(
|
||||||
|
extensions: [
|
||||||
|
StackColors.fromStackColorTheme(LightColors()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
home: Material(
|
||||||
|
child: notificationCard,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(find.byWidget(notificationCard), findsOneWidget);
|
||||||
|
expect(find.text("notification title"), findsOneWidget);
|
||||||
|
expect(find.text("notification description"), findsOneWidget);
|
||||||
|
expect(find.byType(SvgPicture), findsOneWidget);
|
||||||
|
});
|
||||||
|
}
|
157
test/pages/send_view/send_view_test.dart
Normal file
157
test/pages/send_view/send_view_test.dart
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:mockito/annotations.dart';
|
||||||
|
import 'package:mockito/mockito.dart';
|
||||||
|
import 'package:stackwallet/models/send_view_auto_fill_data.dart';
|
||||||
|
import 'package:stackwallet/pages/send_view/send_view.dart';
|
||||||
|
import 'package:stackwallet/providers/providers.dart';
|
||||||
|
import 'package:stackwallet/services/coins/bitcoin/bitcoin_wallet.dart';
|
||||||
|
import 'package:stackwallet/services/coins/coin_service.dart';
|
||||||
|
import 'package:stackwallet/services/coins/manager.dart';
|
||||||
|
import 'package:stackwallet/services/locale_service.dart';
|
||||||
|
import 'package:stackwallet/services/node_service.dart';
|
||||||
|
import 'package:stackwallet/services/wallets.dart';
|
||||||
|
import 'package:stackwallet/services/wallets_service.dart';
|
||||||
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
|
import 'package:stackwallet/utilities/prefs.dart';
|
||||||
|
import 'package:stackwallet/utilities/theme/light_colors.dart';
|
||||||
|
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||||
|
import 'send_view_test.mocks.dart';
|
||||||
|
|
||||||
|
@GenerateMocks([
|
||||||
|
Wallets,
|
||||||
|
WalletsService,
|
||||||
|
NodeService,
|
||||||
|
BitcoinWallet,
|
||||||
|
LocaleService,
|
||||||
|
Prefs,
|
||||||
|
], customMocks: [
|
||||||
|
MockSpec<Manager>(returnNullOnMissingStub: true),
|
||||||
|
MockSpec<CoinServiceAPI>(returnNullOnMissingStub: true),
|
||||||
|
])
|
||||||
|
void main() {
|
||||||
|
testWidgets("Send to valid address", (widgetTester) async {
|
||||||
|
final mockWallets = MockWallets();
|
||||||
|
final mockWalletsService = MockWalletsService();
|
||||||
|
final mockNodeService = MockNodeService();
|
||||||
|
final CoinServiceAPI wallet = MockBitcoinWallet();
|
||||||
|
final mockLocaleService = MockLocaleService();
|
||||||
|
final mockPrefs = MockPrefs();
|
||||||
|
|
||||||
|
when(wallet.coin).thenAnswer((_) => Coin.bitcoin);
|
||||||
|
when(wallet.walletName).thenAnswer((_) => "some wallet");
|
||||||
|
when(wallet.walletId).thenAnswer((_) => "wallet id");
|
||||||
|
|
||||||
|
final manager = Manager(wallet);
|
||||||
|
when(mockWallets.getManagerProvider("wallet id")).thenAnswer(
|
||||||
|
(realInvocation) => ChangeNotifierProvider((ref) => manager));
|
||||||
|
when(mockWallets.getManager("wallet id"))
|
||||||
|
.thenAnswer((realInvocation) => manager);
|
||||||
|
|
||||||
|
when(mockLocaleService.locale).thenAnswer((_) => "en_US");
|
||||||
|
when(mockPrefs.currency).thenAnswer((_) => "USD");
|
||||||
|
when(wallet.validateAddress("send to address"))
|
||||||
|
.thenAnswer((realInvocation) => true);
|
||||||
|
|
||||||
|
await widgetTester.pumpWidget(
|
||||||
|
ProviderScope(
|
||||||
|
overrides: [
|
||||||
|
walletsChangeNotifierProvider.overrideWithValue(mockWallets),
|
||||||
|
walletsServiceChangeNotifierProvider
|
||||||
|
.overrideWithValue(mockWalletsService),
|
||||||
|
nodeServiceChangeNotifierProvider.overrideWithValue(mockNodeService),
|
||||||
|
localeServiceChangeNotifierProvider
|
||||||
|
.overrideWithValue(mockLocaleService),
|
||||||
|
prefsChangeNotifierProvider.overrideWithValue(mockPrefs),
|
||||||
|
// previewTxButtonStateProvider
|
||||||
|
],
|
||||||
|
child: MaterialApp(
|
||||||
|
theme: ThemeData(
|
||||||
|
extensions: [
|
||||||
|
StackColors.fromStackColorTheme(
|
||||||
|
LightColors(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
home: SendView(
|
||||||
|
walletId: "wallet id",
|
||||||
|
coin: Coin.bitcoin,
|
||||||
|
autoFillData: SendViewAutoFillData(
|
||||||
|
address: "send to address", contactLabel: "contact label"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(find.text("Send to"), findsOneWidget);
|
||||||
|
expect(find.text("Amount"), findsOneWidget);
|
||||||
|
expect(find.text("Note (optional)"), findsOneWidget);
|
||||||
|
expect(find.text("Transaction fee (estimated)"), findsOneWidget);
|
||||||
|
verify(manager.validateAddress("send to address")).called(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets("Send to invalid address", (widgetTester) async {
|
||||||
|
final mockWallets = MockWallets();
|
||||||
|
final mockWalletsService = MockWalletsService();
|
||||||
|
final mockNodeService = MockNodeService();
|
||||||
|
final CoinServiceAPI wallet = MockBitcoinWallet();
|
||||||
|
final mockLocaleService = MockLocaleService();
|
||||||
|
final mockPrefs = MockPrefs();
|
||||||
|
|
||||||
|
when(wallet.coin).thenAnswer((_) => Coin.bitcoin);
|
||||||
|
when(wallet.walletName).thenAnswer((_) => "some wallet");
|
||||||
|
when(wallet.walletId).thenAnswer((_) => "wallet id");
|
||||||
|
|
||||||
|
final manager = Manager(wallet);
|
||||||
|
when(mockWallets.getManagerProvider("wallet id")).thenAnswer(
|
||||||
|
(realInvocation) => ChangeNotifierProvider((ref) => manager));
|
||||||
|
when(mockWallets.getManager("wallet id"))
|
||||||
|
.thenAnswer((realInvocation) => manager);
|
||||||
|
|
||||||
|
when(mockLocaleService.locale).thenAnswer((_) => "en_US");
|
||||||
|
when(mockPrefs.currency).thenAnswer((_) => "USD");
|
||||||
|
when(wallet.validateAddress("send to address"))
|
||||||
|
.thenAnswer((realInvocation) => false);
|
||||||
|
|
||||||
|
// when(manager.isOwnAddress("send to address"))
|
||||||
|
// .thenAnswer((realInvocation) => Future(() => true));
|
||||||
|
|
||||||
|
await widgetTester.pumpWidget(
|
||||||
|
ProviderScope(
|
||||||
|
overrides: [
|
||||||
|
walletsChangeNotifierProvider.overrideWithValue(mockWallets),
|
||||||
|
walletsServiceChangeNotifierProvider
|
||||||
|
.overrideWithValue(mockWalletsService),
|
||||||
|
nodeServiceChangeNotifierProvider.overrideWithValue(mockNodeService),
|
||||||
|
localeServiceChangeNotifierProvider
|
||||||
|
.overrideWithValue(mockLocaleService),
|
||||||
|
prefsChangeNotifierProvider.overrideWithValue(mockPrefs),
|
||||||
|
// previewTxButtonStateProvider
|
||||||
|
],
|
||||||
|
child: MaterialApp(
|
||||||
|
theme: ThemeData(
|
||||||
|
extensions: [
|
||||||
|
StackColors.fromStackColorTheme(
|
||||||
|
LightColors(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
home: SendView(
|
||||||
|
walletId: "wallet id",
|
||||||
|
coin: Coin.bitcoin,
|
||||||
|
autoFillData: SendViewAutoFillData(
|
||||||
|
address: "send to address", contactLabel: "contact label"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(find.text("Send to"), findsOneWidget);
|
||||||
|
expect(find.text("Amount"), findsOneWidget);
|
||||||
|
expect(find.text("Note (optional)"), findsOneWidget);
|
||||||
|
expect(find.text("Transaction fee (estimated)"), findsOneWidget);
|
||||||
|
expect(find.text("Invalid address"), findsOneWidget);
|
||||||
|
verify(manager.validateAddress("send to address")).called(1);
|
||||||
|
});
|
||||||
|
}
|
2626
test/pages/send_view/send_view_test.mocks.dart
Normal file
2626
test/pages/send_view/send_view_test.mocks.dart
Normal file
File diff suppressed because it is too large
Load diff
|
@ -16,6 +16,9 @@ void main() {
|
||||||
setUp(() async {
|
setUp(() async {
|
||||||
await setUpTestHive();
|
await setUpTestHive();
|
||||||
await Hive.openBox<dynamic>(DB.boxNamePriceCache);
|
await Hive.openBox<dynamic>(DB.boxNamePriceCache);
|
||||||
|
await Hive.openBox<dynamic>(DB.boxNamePrefs);
|
||||||
|
await DB.instance.put<dynamic>(
|
||||||
|
boxName: DB.boxNamePrefs, key: "externalCalls", value: true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("getPricesAnd24hChange fetch", () async {
|
test("getPricesAnd24hChange fetch", () async {
|
||||||
|
|
|
@ -26,7 +26,7 @@ import 'bitcoincash_wallet_test_parameters.dart';
|
||||||
void main() {
|
void main() {
|
||||||
group("bitcoincash constants", () {
|
group("bitcoincash constants", () {
|
||||||
test("bitcoincash minimum confirmations", () async {
|
test("bitcoincash minimum confirmations", () async {
|
||||||
expect(MINIMUM_CONFIRMATIONS, 3);
|
expect(MINIMUM_CONFIRMATIONS, 1);
|
||||||
});
|
});
|
||||||
test("bitcoincash dust limit", () async {
|
test("bitcoincash dust limit", () async {
|
||||||
expect(DUST_LIMIT, 546);
|
expect(DUST_LIMIT, 546);
|
||||||
|
@ -831,18 +831,9 @@ void main() {
|
||||||
|
|
||||||
await bch?.initializeNew();
|
await bch?.initializeNew();
|
||||||
await bch?.initializeExisting();
|
await bch?.initializeExisting();
|
||||||
expect(
|
expect(bch?.validateAddress(await bch!.currentReceivingAddress), true);
|
||||||
Address.validateAddress(
|
expect(bch?.validateAddress(await bch!.currentReceivingAddress), true);
|
||||||
await bch!.currentReceivingAddress, bitcoincashtestnet),
|
expect(bch?.validateAddress(await bch!.currentReceivingAddress), true);
|
||||||
true);
|
|
||||||
expect(
|
|
||||||
Address.validateAddress(
|
|
||||||
await bch!.currentReceivingAddress, bitcoincashtestnet),
|
|
||||||
true);
|
|
||||||
expect(
|
|
||||||
Address.validateAddress(
|
|
||||||
await bch!.currentReceivingAddress, bitcoincashtestnet),
|
|
||||||
true);
|
|
||||||
|
|
||||||
verifyNever(client?.ping()).called(0);
|
verifyNever(client?.ping()).called(0);
|
||||||
verify(client?.getServerFeatures()).called(1);
|
verify(client?.getServerFeatures()).called(1);
|
||||||
|
@ -884,8 +875,7 @@ void main() {
|
||||||
expect(addresses?.length, 2);
|
expect(addresses?.length, 2);
|
||||||
|
|
||||||
for (int i = 0; i < 2; i++) {
|
for (int i = 0; i < 2; i++) {
|
||||||
expect(
|
expect(bch?.validateAddress(addresses![i]), true);
|
||||||
Address.validateAddress(addresses![i], bitcoincashtestnet), true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
verifyNever(client?.ping()).called(0);
|
verifyNever(client?.ping()).called(0);
|
||||||
|
|
|
@ -21,207 +21,46 @@ class MockedFunctions extends Mock {
|
||||||
|
|
||||||
@GenerateMocks([AddressBookService])
|
@GenerateMocks([AddressBookService])
|
||||||
void main() {
|
void main() {
|
||||||
group('Navigation tests', () {
|
testWidgets('test returns Contact Address Entry', (widgetTester) async {
|
||||||
late AddressBookService service;
|
final service = MockAddressBookService();
|
||||||
setUp(() {
|
|
||||||
service = MockAddressBookService();
|
|
||||||
|
|
||||||
when(service.getContactById("some id"))
|
when(service.getContactById("default"))
|
||||||
.thenAnswer((realInvocation) => Contact(
|
.thenAnswer((realInvocation) => Contact(
|
||||||
name: "John Doe",
|
name: "John Doe",
|
||||||
addresses: [
|
addresses: [
|
||||||
const ContactAddressEntry(
|
const ContactAddressEntry(
|
||||||
coin: Coin.bitcoincash,
|
coin: Coin.bitcoincash,
|
||||||
address: "some bch address",
|
address: "some bch address",
|
||||||
label: "Bills")
|
label: "Bills")
|
||||||
],
|
],
|
||||||
isFavorite: true));
|
isFavorite: true));
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('test returns Contact Address Entry', (widgetTester) async {
|
await widgetTester.pumpWidget(
|
||||||
await widgetTester.pumpWidget(
|
ProviderScope(
|
||||||
ProviderScope(
|
overrides: [
|
||||||
overrides: [
|
addressBookServiceProvider.overrideWithValue(
|
||||||
addressBookServiceProvider.overrideWithValue(
|
service,
|
||||||
service,
|
),
|
||||||
),
|
],
|
||||||
],
|
child: MaterialApp(
|
||||||
child: MaterialApp(
|
theme: ThemeData(
|
||||||
theme: ThemeData(
|
extensions: [
|
||||||
extensions: [
|
StackColors.fromStackColorTheme(
|
||||||
StackColors.fromStackColorTheme(
|
LightColors(),
|
||||||
LightColors(),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
home: const AddressBookCard(
|
||||||
home: const AddressBookCard(
|
contactId: "default",
|
||||||
contactId: "some id",
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
|
);
|
||||||
|
|
||||||
expect(find.text("John Doe"), findsOneWidget);
|
expect(find.text("John Doe"), findsOneWidget);
|
||||||
expect(find.text(Coin.bitcoincash.ticker), findsOneWidget);
|
expect(find.text("BCH"), findsOneWidget);
|
||||||
});
|
expect(find.text(Coin.bitcoincash.ticker), findsOneWidget);
|
||||||
|
|
||||||
// testWidgets("Test button press opens dialog", (widgetTester) async {
|
await widgetTester.tap(find.byType(RawMaterialButton));
|
||||||
// // final service = MockAddressBookService();
|
|
||||||
//
|
|
||||||
// when(service.getContactById("some id"))
|
|
||||||
// .thenAnswer((realInvocation) => Contact(
|
|
||||||
// name: "John Doe",
|
|
||||||
// addresses: [
|
|
||||||
// const ContactAddressEntry(
|
|
||||||
// coin: Coin.bitcoincash,
|
|
||||||
// address: "some bch address",
|
|
||||||
// label: "Bills")
|
|
||||||
// ],
|
|
||||||
// isFavorite: true));
|
|
||||||
//
|
|
||||||
// await widgetTester.pumpWidget(
|
|
||||||
// ProviderScope(
|
|
||||||
// overrides: [
|
|
||||||
// addressBookServiceProvider.overrideWithValue(
|
|
||||||
// service,
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// child: MaterialApp(
|
|
||||||
// theme: ThemeData(
|
|
||||||
// extensions: [
|
|
||||||
// StackColors.fromStackColorTheme(
|
|
||||||
// LightColors(),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// home: const AddressBookCard(
|
|
||||||
// contactId: "some id",
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// //
|
|
||||||
// // when(service.getContactById("03177ce0-4af4-11ed-9617-af8aa7a3796f"))
|
|
||||||
// // .thenAnswer((realInvocation) => Contact(
|
|
||||||
// // name: "John Doe",
|
|
||||||
// // addresses: [
|
|
||||||
// // const ContactAddressEntry(
|
|
||||||
// // coin: Coin.bitcoincash,
|
|
||||||
// // address: "some bch address",
|
|
||||||
// // label: "Bills")
|
|
||||||
// // ],
|
|
||||||
// // isFavorite: true));
|
|
||||||
// await widgetTester.tap(find.byType(RawMaterialButton));
|
|
||||||
// // verify(MockedFunctions().showDialog()).called(1);
|
|
||||||
// await widgetTester.pump();
|
|
||||||
// when(service.getContactById("03177ce0-4af4-11ed-9617-af8aa7a3796f"))
|
|
||||||
// .thenAnswer((realInvocation) => Contact(
|
|
||||||
// name: "John Doe",
|
|
||||||
// addresses: [
|
|
||||||
// const ContactAddressEntry(
|
|
||||||
// coin: Coin.bitcoincash,
|
|
||||||
// address: "some bch address",
|
|
||||||
// label: "Bills")
|
|
||||||
// ],
|
|
||||||
// isFavorite: true));
|
|
||||||
//
|
|
||||||
// expect(
|
|
||||||
// find.byWidget(const ContactPopUp(
|
|
||||||
// contactId: "03177ce0-4af4-11ed-9617-af8aa7a3796f")),
|
|
||||||
// findsOneWidget);
|
|
||||||
// // await widgetTester.pump();
|
|
||||||
// // // when(contact)
|
|
||||||
// // await widgetTester.pump();
|
|
||||||
// });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// testWidgets('test returns Contact Address Entry', (widgetTester) async {
|
|
||||||
// // final service = MockAddressBookService();
|
|
||||||
// // when(service.getContactById("some id"))
|
|
||||||
// // .thenAnswer((realInvocation) => Contact(
|
|
||||||
// // name: "John Doe",
|
|
||||||
// // addresses: [
|
|
||||||
// // const ContactAddressEntry(
|
|
||||||
// // coin: Coin.bitcoincash,
|
|
||||||
// // address: "some bch address",
|
|
||||||
// // label: "Bills")
|
|
||||||
// // ],
|
|
||||||
// // isFavorite: true));
|
|
||||||
//
|
|
||||||
// await widgetTester.pumpWidget(
|
|
||||||
// ProviderScope(
|
|
||||||
// overrides: [
|
|
||||||
// addressBookServiceProvider.overrideWithValue(
|
|
||||||
// serv,
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// child: MaterialApp(
|
|
||||||
// theme: ThemeData(
|
|
||||||
// extensions: [
|
|
||||||
// StackColors.fromStackColorTheme(
|
|
||||||
// LightColors(),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// home: const AddressBookCard(
|
|
||||||
// contactId: "some id",
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// expect(find.text("John Doe"), findsOneWidget);
|
|
||||||
// expect(find.text(Coin.bitcoincash.ticker), findsOneWidget);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// testWidgets("Test button press opens dialog", (widgetTester) async {
|
|
||||||
// final service = MockAddressBookService();
|
|
||||||
//
|
|
||||||
// // when(service.getContactById("some id"))
|
|
||||||
// // .thenAnswer((realInvocation) => Contact(
|
|
||||||
// // name: "John Doe",
|
|
||||||
// // addresses: [
|
|
||||||
// // const ContactAddressEntry(
|
|
||||||
// // coin: Coin.bitcoincash,
|
|
||||||
// // address: "some bch address",
|
|
||||||
// // label: "Bills")
|
|
||||||
// // ],
|
|
||||||
// // isFavorite: true));
|
|
||||||
//
|
|
||||||
// await widgetTester.pumpWidget(
|
|
||||||
// ProviderScope(
|
|
||||||
// overrides: [
|
|
||||||
// addressBookServiceProvider.overrideWithValue(
|
|
||||||
// service,
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// child: MaterialApp(
|
|
||||||
// theme: ThemeData(
|
|
||||||
// extensions: [
|
|
||||||
// StackColors.fromStackColorTheme(
|
|
||||||
// LightColors(),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// home: const AddressBookCard(
|
|
||||||
// contactId: "03177ce0-4af4-11ed-9617-af8aa7a3796f",
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// //
|
|
||||||
// // when(service.getContactById("03177ce0-4af4-11ed-9617-af8aa7a3796f"))
|
|
||||||
// // .thenAnswer((realInvocation) => Contact(
|
|
||||||
// // name: "John Doe",
|
|
||||||
// // addresses: [
|
|
||||||
// // const ContactAddressEntry(
|
|
||||||
// // coin: Coin.bitcoincash,
|
|
||||||
// // address: "some bch address",
|
|
||||||
// // label: "Bills")
|
|
||||||
// // ],
|
|
||||||
// // isFavorite: true));
|
|
||||||
// // await widgetTester.tap(find.byType(RawMaterialButton));
|
|
||||||
// // // when(contact)
|
|
||||||
// // await widgetTester.pump();
|
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
|
|
35
test/widget_tests/custom_buttons/favorite_toggle_test.dart
Normal file
35
test/widget_tests/custom_buttons/favorite_toggle_test.dart
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:stackwallet/utilities/theme/light_colors.dart';
|
||||||
|
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:stackwallet/widgets/custom_buttons/favorite_toggle.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets("Test widget build", (widgetTester) async {
|
||||||
|
final key = UniqueKey();
|
||||||
|
|
||||||
|
await widgetTester.pumpWidget(
|
||||||
|
ProviderScope(
|
||||||
|
overrides: [],
|
||||||
|
child: MaterialApp(
|
||||||
|
theme: ThemeData(
|
||||||
|
extensions: [
|
||||||
|
StackColors.fromStackColorTheme(
|
||||||
|
LightColors(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
home: FavoriteToggle(
|
||||||
|
onChanged: null,
|
||||||
|
key: key,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(find.byType(FavoriteToggle), findsOneWidget);
|
||||||
|
expect(find.byType(SvgPicture), findsOneWidget);
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:event_bus/event_bus.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:stackwallet/utilities/theme/light_colors.dart';
|
import 'package:stackwallet/utilities/theme/light_colors.dart';
|
||||||
|
@ -6,8 +7,7 @@ import 'package:stackwallet/widgets/custom_loading_overlay.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
testWidgets("Test wiget displays correct text", (widgetTester) async {
|
testWidgets("Test wiget displays correct text", (widgetTester) async {
|
||||||
const customLoadingOverlay =
|
final eventBus = EventBus();
|
||||||
CustomLoadingOverlay(message: "Updating exchange rate", eventBus: null);
|
|
||||||
await widgetTester.pumpWidget(
|
await widgetTester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
|
@ -15,8 +15,9 @@ void main() {
|
||||||
StackColors.fromStackColorTheme(LightColors()),
|
StackColors.fromStackColorTheme(LightColors()),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
home: const Material(
|
home: Material(
|
||||||
child: customLoadingOverlay,
|
child: CustomLoadingOverlay(
|
||||||
|
message: "Updating exchange rate", eventBus: eventBus),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
33
test/widget_tests/desktop/custom_text_button_test.dart
Normal file
33
test/widget_tests/desktop/custom_text_button_test.dart
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:stackwallet/utilities/theme/light_colors.dart';
|
||||||
|
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||||
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
|
import 'package:stackwallet/widgets/desktop/custom_text_button.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets("Test text button ", (widgetTester) async {
|
||||||
|
final key = UniqueKey();
|
||||||
|
|
||||||
|
await widgetTester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: ThemeData(
|
||||||
|
extensions: [
|
||||||
|
StackColors.fromStackColorTheme(LightColors()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
home: Material(
|
||||||
|
child: CustomTextButtonBase(
|
||||||
|
key: key,
|
||||||
|
width: 200,
|
||||||
|
height: 300,
|
||||||
|
textButton:
|
||||||
|
const TextButton(onPressed: null, child: Text("Some Text")),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(find.byType(CustomTextButtonBase), findsOneWidget);
|
||||||
|
});
|
||||||
|
}
|
34
test/widget_tests/desktop/desktop_app_bar_test.dart
Normal file
34
test/widget_tests/desktop/desktop_app_bar_test.dart
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/exit_to_my_stack_button.dart';
|
||||||
|
import 'package:stackwallet/utilities/theme/light_colors.dart';
|
||||||
|
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||||
|
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||||
|
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets("Test DesktopAppBar widget", (widgetTester) async {
|
||||||
|
final key = UniqueKey();
|
||||||
|
|
||||||
|
await widgetTester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: ThemeData(
|
||||||
|
extensions: [
|
||||||
|
StackColors.fromStackColorTheme(LightColors()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
home: Material(
|
||||||
|
child: DesktopAppBar(
|
||||||
|
key: key,
|
||||||
|
isCompactHeight: false,
|
||||||
|
leading: const AppBarBackButton(),
|
||||||
|
trailing: const ExitToMyStackButton(),
|
||||||
|
center: const Text("Some Text"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(find.byType(DesktopAppBar), findsOneWidget);
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:mockingjay/mockingjay.dart' as mockingjay;
|
||||||
|
import 'package:stackwallet/utilities/theme/light_colors.dart';
|
||||||
|
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||||
|
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||||
|
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets("test DesktopDialog button pressed", (widgetTester) async {
|
||||||
|
final key = UniqueKey();
|
||||||
|
|
||||||
|
final navigator = mockingjay.MockNavigator();
|
||||||
|
|
||||||
|
await widgetTester.pumpWidget(
|
||||||
|
ProviderScope(
|
||||||
|
overrides: [],
|
||||||
|
child: MaterialApp(
|
||||||
|
theme: ThemeData(
|
||||||
|
extensions: [
|
||||||
|
StackColors.fromStackColorTheme(LightColors()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
home: mockingjay.MockNavigatorProvider(
|
||||||
|
navigator: navigator,
|
||||||
|
child: DesktopDialogCloseButton(
|
||||||
|
key: key,
|
||||||
|
onPressedOverride: null,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await widgetTester.tap(find.byType(AppBarIconButton));
|
||||||
|
await widgetTester.pumpAndSettle();
|
||||||
|
|
||||||
|
mockingjay.verify(() => navigator.pop()).called(1);
|
||||||
|
});
|
||||||
|
}
|
30
test/widget_tests/desktop/desktop_dialog_test.dart
Normal file
30
test/widget_tests/desktop/desktop_dialog_test.dart
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:stackwallet/utilities/theme/light_colors.dart';
|
||||||
|
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||||
|
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||||
|
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets("test DesktopDialog builds", (widgetTester) async {
|
||||||
|
final key = UniqueKey();
|
||||||
|
|
||||||
|
await widgetTester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: ThemeData(
|
||||||
|
extensions: [
|
||||||
|
StackColors.fromStackColorTheme(LightColors()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
home: Material(
|
||||||
|
child: DesktopDialog(
|
||||||
|
key: key,
|
||||||
|
child: const DesktopDialogCloseButton(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(find.byType(DesktopDialog), findsOneWidget);
|
||||||
|
});
|
||||||
|
}
|
70
test/widget_tests/desktop/desktop_scaffold_test.dart
Normal file
70
test/widget_tests/desktop/desktop_scaffold_test.dart
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:stackwallet/utilities/theme/light_colors.dart';
|
||||||
|
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||||
|
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets("test DesktopScaffold", (widgetTester) async {
|
||||||
|
final key = UniqueKey();
|
||||||
|
await widgetTester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: ThemeData(
|
||||||
|
extensions: [
|
||||||
|
StackColors.fromStackColorTheme(LightColors()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
home: Material(
|
||||||
|
child: DesktopScaffold(
|
||||||
|
key: key,
|
||||||
|
body: const SizedBox(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets("Test MasterScaffold for non desktop", (widgetTester) async {
|
||||||
|
final key = UniqueKey();
|
||||||
|
|
||||||
|
await widgetTester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: ThemeData(
|
||||||
|
extensions: [
|
||||||
|
StackColors.fromStackColorTheme(LightColors()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
home: Material(
|
||||||
|
child: MasterScaffold(
|
||||||
|
key: key,
|
||||||
|
body: const SizedBox(),
|
||||||
|
appBar: AppBar(),
|
||||||
|
isDesktop: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets("Test MasterScaffold for desktop", (widgetTester) async {
|
||||||
|
final key = UniqueKey();
|
||||||
|
|
||||||
|
await widgetTester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: ThemeData(
|
||||||
|
extensions: [
|
||||||
|
StackColors.fromStackColorTheme(LightColors()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
home: Material(
|
||||||
|
child: MasterScaffold(
|
||||||
|
key: key,
|
||||||
|
body: const SizedBox(),
|
||||||
|
appBar: AppBar(),
|
||||||
|
isDesktop: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
31
test/widget_tests/icon_widgets/addressbook_icon_test.dart
Normal file
31
test/widget_tests/icon_widgets/addressbook_icon_test.dart
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:stackwallet/utilities/theme/light_colors.dart';
|
||||||
|
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||||
|
import 'package:stackwallet/widgets/icon_widgets/addressbook_icon.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets("test address book icon widget", (widgetTester) async {
|
||||||
|
final key = UniqueKey();
|
||||||
|
final addressBookIcon = AddressBookIcon(
|
||||||
|
key: key,
|
||||||
|
);
|
||||||
|
|
||||||
|
await widgetTester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: ThemeData(
|
||||||
|
extensions: [
|
||||||
|
StackColors.fromStackColorTheme(LightColors()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
home: Material(
|
||||||
|
child: addressBookIcon,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(find.byWidget(addressBookIcon), findsOneWidget);
|
||||||
|
expect(find.byType(SvgPicture), findsOneWidget);
|
||||||
|
});
|
||||||
|
}
|
|
@ -18,6 +18,7 @@ import 'package:stackwallet/widgets/managed_favorite.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:stackwallet/utilities/theme/light_colors.dart';
|
import 'package:stackwallet/utilities/theme/light_colors.dart';
|
||||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||||
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
|
|
||||||
import 'managed_favorite_test.mocks.dart';
|
import 'managed_favorite_test.mocks.dart';
|
||||||
|
|
||||||
|
@ -45,7 +46,8 @@ void main() {
|
||||||
.thenAnswer((realInvocation) => manager);
|
.thenAnswer((realInvocation) => manager);
|
||||||
|
|
||||||
when(manager.isFavorite).thenAnswer((realInvocation) => false);
|
when(manager.isFavorite).thenAnswer((realInvocation) => false);
|
||||||
const managedFavorite = ManagedFavorite(walletId: "some wallet id");
|
final key = UniqueKey();
|
||||||
|
// const managedFavorite = ManagedFavorite(walletId: "some wallet id", key: key,);
|
||||||
await widgetTester.pumpWidget(
|
await widgetTester.pumpWidget(
|
||||||
ProviderScope(
|
ProviderScope(
|
||||||
overrides: [
|
overrides: [
|
||||||
|
@ -59,8 +61,11 @@ void main() {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
home: const Material(
|
home: Material(
|
||||||
child: managedFavorite,
|
child: ManagedFavorite(
|
||||||
|
walletId: "some wallet id",
|
||||||
|
key: key,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -82,7 +82,7 @@ void main() {
|
||||||
(realInvocation) => NodeModel(
|
(realInvocation) => NodeModel(
|
||||||
host: "127.0.0.1",
|
host: "127.0.0.1",
|
||||||
port: 2000,
|
port: 2000,
|
||||||
name: "Stack Default",
|
name: "Some other node name",
|
||||||
id: "node id",
|
id: "node id",
|
||||||
useSSL: true,
|
useSSL: true,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
@ -94,7 +94,7 @@ void main() {
|
||||||
NodeModel(
|
NodeModel(
|
||||||
host: "127.0.0.1",
|
host: "127.0.0.1",
|
||||||
port: 2000,
|
port: 2000,
|
||||||
name: "Stack Default",
|
name: "Some other node name",
|
||||||
id: "node id",
|
id: "node id",
|
||||||
useSSL: true,
|
useSSL: true,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
@ -122,7 +122,7 @@ void main() {
|
||||||
);
|
);
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
expect(find.text("Stack Default"), findsOneWidget);
|
expect(find.text("Some other node name"), findsOneWidget);
|
||||||
expect(find.text("Connected"), findsOneWidget);
|
expect(find.text("Connected"), findsOneWidget);
|
||||||
expect(find.byType(Text), findsNWidgets(2));
|
expect(find.byType(Text), findsNWidgets(2));
|
||||||
expect(find.byType(SvgPicture), findsWidgets);
|
expect(find.byType(SvgPicture), findsWidgets);
|
||||||
|
|
|
@ -29,7 +29,7 @@ void main() {
|
||||||
(realInvocation) => NodeModel(
|
(realInvocation) => NodeModel(
|
||||||
host: "127.0.0.1",
|
host: "127.0.0.1",
|
||||||
port: 2000,
|
port: 2000,
|
||||||
name: "Stack Default",
|
name: "Some other name",
|
||||||
id: "node id",
|
id: "node id",
|
||||||
useSSL: true,
|
useSSL: true,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
@ -41,7 +41,7 @@ void main() {
|
||||||
(realInvocation) => NodeModel(
|
(realInvocation) => NodeModel(
|
||||||
host: "127.0.0.1",
|
host: "127.0.0.1",
|
||||||
port: 2000,
|
port: 2000,
|
||||||
name: "Stack Default",
|
name: "Some other name",
|
||||||
id: "node id",
|
id: "node id",
|
||||||
useSSL: true,
|
useSSL: true,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
@ -72,7 +72,7 @@ void main() {
|
||||||
|
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
expect(find.text("Node options"), findsOneWidget);
|
expect(find.text("Node options"), findsOneWidget);
|
||||||
expect(find.text("Stack Default"), findsOneWidget);
|
expect(find.text("Some other name"), findsOneWidget);
|
||||||
expect(find.text("Connected"), findsOneWidget);
|
expect(find.text("Connected"), findsOneWidget);
|
||||||
expect(find.byType(SvgPicture), findsNWidgets(2));
|
expect(find.byType(SvgPicture), findsNWidgets(2));
|
||||||
expect(find.text("Details"), findsOneWidget);
|
expect(find.text("Details"), findsOneWidget);
|
||||||
|
@ -204,7 +204,6 @@ void main() {
|
||||||
|
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
expect(find.text("Node options"), findsOneWidget);
|
expect(find.text("Node options"), findsOneWidget);
|
||||||
// expect(find.text("Stack Default"), findsOneWidget);
|
|
||||||
expect(find.text("Disconnected"), findsOneWidget);
|
expect(find.text("Disconnected"), findsOneWidget);
|
||||||
|
|
||||||
await tester.tap(find.text("Connect"));
|
await tester.tap(find.text("Connect"));
|
||||||
|
|
35
test/widget_tests/shake/shake_test.dart
Normal file
35
test/widget_tests/shake/shake_test.dart
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:stackwallet/utilities/theme/light_colors.dart';
|
||||||
|
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||||
|
import 'package:stackwallet/widgets/shake/shake.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets("Widget build", (widgetTester) async {
|
||||||
|
await widgetTester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: ThemeData(
|
||||||
|
extensions: [
|
||||||
|
StackColors.fromStackColorTheme(LightColors()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
home: Material(
|
||||||
|
child: Shake(
|
||||||
|
animationRange: 10,
|
||||||
|
controller: ShakeController(),
|
||||||
|
animationDuration: const Duration(milliseconds: 200),
|
||||||
|
child: Column(
|
||||||
|
children: const [
|
||||||
|
Center(
|
||||||
|
child: Text("Enter Pin"),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(find.byType(Shake), findsOneWidget);
|
||||||
|
expect(find.byType(Text), findsOneWidget);
|
||||||
|
});
|
||||||
|
}
|
55
test/widget_tests/trade_card_test.dart
Normal file
55
test/widget_tests/trade_card_test.dart
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:stackwallet/models/exchange/response_objects/trade.dart';
|
||||||
|
import 'package:stackwallet/utilities/theme/light_colors.dart';
|
||||||
|
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||||
|
import 'package:stackwallet/widgets/trade_card.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets("Test Trade card builds", (widgetTester) async {
|
||||||
|
final trade = Trade(
|
||||||
|
uuid: "uuid",
|
||||||
|
tradeId: "trade id",
|
||||||
|
rateType: "Estimate rate",
|
||||||
|
direction: "",
|
||||||
|
timestamp: DateTime.parse("1662544771"),
|
||||||
|
updatedAt: DateTime.parse("1662544771"),
|
||||||
|
payInCurrency: "BTC",
|
||||||
|
payInAmount: "10",
|
||||||
|
payInAddress: "btc address",
|
||||||
|
payInNetwork: "",
|
||||||
|
payInExtraId: "",
|
||||||
|
payInTxid: "",
|
||||||
|
payOutCurrency: "xmr",
|
||||||
|
payOutAmount: "10",
|
||||||
|
payOutAddress: "xmr address",
|
||||||
|
payOutNetwork: "",
|
||||||
|
payOutExtraId: "",
|
||||||
|
payOutTxid: "",
|
||||||
|
refundAddress: "refund address",
|
||||||
|
refundExtraId: "",
|
||||||
|
status: "Failed",
|
||||||
|
exchangeName: "Some Exchange");
|
||||||
|
|
||||||
|
await widgetTester.pumpWidget(
|
||||||
|
ProviderScope(
|
||||||
|
overrides: [],
|
||||||
|
child: MaterialApp(
|
||||||
|
theme: ThemeData(
|
||||||
|
extensions: [
|
||||||
|
StackColors.fromStackColorTheme(
|
||||||
|
LightColors(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
home: TradeCard(trade: trade, onTap: () {}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(find.byType(TradeCard), findsOneWidget);
|
||||||
|
expect(find.text("BTC → XMR"), findsOneWidget);
|
||||||
|
expect(find.text("Some Exchange"), findsOneWidget);
|
||||||
|
});
|
||||||
|
}
|
|
@ -131,6 +131,99 @@ void main() {
|
||||||
verifyNoMoreInteractions(mockLocaleService);
|
verifyNoMoreInteractions(mockLocaleService);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets("Anonymized confirmed tx displays correctly", (tester) async {
|
||||||
|
final mockManager = MockManager();
|
||||||
|
final mockLocaleService = MockLocaleService();
|
||||||
|
final wallets = MockWallets();
|
||||||
|
final mockPrefs = MockPrefs();
|
||||||
|
final mockPriceService = MockPriceService();
|
||||||
|
|
||||||
|
final tx = Transaction(
|
||||||
|
txid: "some txid",
|
||||||
|
confirmedStatus: true,
|
||||||
|
timestamp: 1648595998,
|
||||||
|
txType: "Anonymized",
|
||||||
|
amount: 100000000,
|
||||||
|
aliens: [],
|
||||||
|
worthNow: "0.01",
|
||||||
|
worthAtBlockTimestamp: "0.01",
|
||||||
|
fees: 3794,
|
||||||
|
inputSize: 1,
|
||||||
|
outputSize: 1,
|
||||||
|
inputs: [],
|
||||||
|
outputs: [],
|
||||||
|
address: "",
|
||||||
|
height: 450123,
|
||||||
|
subType: "mint",
|
||||||
|
confirmations: 10,
|
||||||
|
isCancelled: false);
|
||||||
|
|
||||||
|
final CoinServiceAPI wallet = MockFiroWallet();
|
||||||
|
|
||||||
|
when(wallet.coin.ticker).thenAnswer((_) => "FIRO");
|
||||||
|
when(mockLocaleService.locale).thenAnswer((_) => "en_US");
|
||||||
|
when(mockPrefs.currency).thenAnswer((_) => "USD");
|
||||||
|
when(mockPrefs.externalCalls).thenAnswer((_) => true);
|
||||||
|
when(mockPriceService.getPrice(Coin.firo))
|
||||||
|
.thenAnswer((realInvocation) => Tuple2(Decimal.ten, 0.00));
|
||||||
|
|
||||||
|
when(wallet.coin).thenAnswer((_) => Coin.firo);
|
||||||
|
|
||||||
|
when(wallets.getManager("wallet-id"))
|
||||||
|
.thenAnswer((realInvocation) => Manager(wallet));
|
||||||
|
//
|
||||||
|
await tester.pumpWidget(
|
||||||
|
ProviderScope(
|
||||||
|
overrides: [
|
||||||
|
walletsChangeNotifierProvider.overrideWithValue(wallets),
|
||||||
|
localeServiceChangeNotifierProvider
|
||||||
|
.overrideWithValue(mockLocaleService),
|
||||||
|
prefsChangeNotifierProvider.overrideWithValue(mockPrefs),
|
||||||
|
priceAnd24hChangeNotifierProvider.overrideWithValue(mockPriceService)
|
||||||
|
],
|
||||||
|
child: MaterialApp(
|
||||||
|
theme: ThemeData(
|
||||||
|
extensions: [
|
||||||
|
StackColors.fromStackColorTheme(
|
||||||
|
LightColors(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
home: TransactionCard(transaction: tx, walletId: "wallet-id"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
//
|
||||||
|
final title = find.text("Anonymized");
|
||||||
|
// final price1 = find.text("0.00 USD");
|
||||||
|
final amount = find.text("1.00000000 FIRO");
|
||||||
|
|
||||||
|
final icon = find.byIcon(FeatherIcons.arrowUp);
|
||||||
|
|
||||||
|
expect(title, findsOneWidget);
|
||||||
|
// expect(price1, findsOneWidget);
|
||||||
|
expect(amount, findsOneWidget);
|
||||||
|
// expect(icon, findsOneWidget);
|
||||||
|
//
|
||||||
|
await tester.pumpAndSettle(Duration(seconds: 2));
|
||||||
|
//
|
||||||
|
// final price2 = find.text("\$10.00");
|
||||||
|
// expect(price2, findsOneWidget);
|
||||||
|
//
|
||||||
|
// verify(mockManager.addListener(any)).called(1);
|
||||||
|
verify(mockLocaleService.addListener(any)).called(1);
|
||||||
|
|
||||||
|
verify(mockPrefs.currency).called(1);
|
||||||
|
verify(mockPriceService.getPrice(Coin.firo)).called(1);
|
||||||
|
verify(wallet.coin.ticker).called(1);
|
||||||
|
|
||||||
|
verify(mockLocaleService.locale).called(1);
|
||||||
|
|
||||||
|
verifyNoMoreInteractions(mockManager);
|
||||||
|
verifyNoMoreInteractions(mockLocaleService);
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets("Received unconfirmed tx displays correctly", (tester) async {
|
testWidgets("Received unconfirmed tx displays correctly", (tester) async {
|
||||||
final mockManager = MockManager();
|
final mockManager = MockManager();
|
||||||
final mockLocaleService = MockLocaleService();
|
final mockLocaleService = MockLocaleService();
|
||||||
|
|
Loading…
Reference in a new issue