Merge pull request #41 from cypherstack/ui-testing

UI testing
This commit is contained in:
Marco Salazar 2022-09-06 23:27:57 +08:00 committed by GitHub
commit 6bfd3d5809
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 1583 additions and 544 deletions

14
assets/svg/share-2.svg Normal file
View file

@ -0,0 +1,14 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5024_11503)">
<path d="M9 4C9.82843 4 10.5 3.32843 10.5 2.5C10.5 1.67157 9.82843 1 9 1C8.17157 1 7.5 1.67157 7.5 2.5C7.5 3.32843 8.17157 4 9 4Z" fill="white" stroke="#111111" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M3 7.5C3.82843 7.5 4.5 6.82843 4.5 6C4.5 5.17157 3.82843 4.5 3 4.5C2.17157 4.5 1.5 5.17157 1.5 6C1.5 6.82843 2.17157 7.5 3 7.5Z" stroke="#111111" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9 11C9.82843 11 10.5 10.3284 10.5 9.5C10.5 8.67157 9.82843 8 9 8C8.17157 8 7.5 8.67157 7.5 9.5C7.5 10.3284 8.17157 11 9 11Z" stroke="#111111" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4.29504 6.75488L7.71004 8.74488" stroke="#111111" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7.70504 3.25488L4.29504 5.24488" stroke="#111111" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_5024_11503">
<rect width="12" height="12" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -27,6 +27,7 @@ class DB {
static const String boxNamePrefs = "prefs"; static const String boxNamePrefs = "prefs";
static const String boxNameWalletsToDeleteOnStart = "walletsToDeleteOnStart"; static const String boxNameWalletsToDeleteOnStart = "walletsToDeleteOnStart";
static const String boxNamePriceCache = "priceAPIPrice24hCache"; static const String boxNamePriceCache = "priceAPIPrice24hCache";
static const String boxNameDBInfo = "dbInfo";
String boxNameTxCache({required Coin coin}) => "${coin.name}_txCache"; String boxNameTxCache({required Coin coin}) => "${coin.name}_txCache";
String boxNameSetCache({required Coin coin}) => String boxNameSetCache({required Coin coin}) =>
@ -50,6 +51,7 @@ class DB {
late final Box<xmr.WalletInfo> _walletInfoSource; late final Box<xmr.WalletInfo> _walletInfoSource;
late final Box<dynamic> _boxPrefs; late final Box<dynamic> _boxPrefs;
late final Box<TradeWalletLookup> _boxTradeLookup; late final Box<TradeWalletLookup> _boxTradeLookup;
late final Box<dynamic> _boxDBInfo;
final Map<String, Box<dynamic>> _walletBoxes = {}; final Map<String, Box<dynamic>> _walletBoxes = {};
@ -80,13 +82,40 @@ class DB {
// open hive boxes // open hive boxes
Future<void> init() async { Future<void> init() async {
if (!_initialized) { if (!_initialized) {
if (Hive.isBoxOpen(boxNameDBInfo)) {
_boxDBInfo = Hive.box<dynamic>(boxNameDBInfo);
} else {
_boxDBInfo = await Hive.openBox<dynamic>(boxNameDBInfo);
}
await Hive.openBox<String>(boxNameWalletsToDeleteOnStart); await Hive.openBox<String>(boxNameWalletsToDeleteOnStart);
if (Hive.isBoxOpen(boxNamePrefs)) {
_boxPrefs = Hive.box<dynamic>(boxNamePrefs);
} else {
_boxPrefs = await Hive.openBox<dynamic>(boxNamePrefs); _boxPrefs = await Hive.openBox<dynamic>(boxNamePrefs);
}
_boxAddressBook = await Hive.openBox<dynamic>(boxNameAddressBook); _boxAddressBook = await Hive.openBox<dynamic>(boxNameAddressBook);
_boxDebugInfo = await Hive.openBox<String>(boxNameDebugInfo); _boxDebugInfo = await Hive.openBox<String>(boxNameDebugInfo);
if (Hive.isBoxOpen(boxNameNodeModels)) {
_boxNodeModels = Hive.box<NodeModel>(boxNameNodeModels);
} else {
_boxNodeModels = await Hive.openBox<NodeModel>(boxNameNodeModels); _boxNodeModels = await Hive.openBox<NodeModel>(boxNameNodeModels);
}
if (Hive.isBoxOpen(boxNamePrimaryNodes)) {
_boxPrimaryNodes = Hive.box<NodeModel>(boxNamePrimaryNodes);
} else {
_boxPrimaryNodes = await Hive.openBox<NodeModel>(boxNamePrimaryNodes); _boxPrimaryNodes = await Hive.openBox<NodeModel>(boxNamePrimaryNodes);
}
if (Hive.isBoxOpen(boxNameAllWalletsData)) {
_boxAllWalletsData = Hive.box<dynamic>(boxNameAllWalletsData);
} else {
_boxAllWalletsData = await Hive.openBox<dynamic>(boxNameAllWalletsData); _boxAllWalletsData = await Hive.openBox<dynamic>(boxNameAllWalletsData);
}
_boxNotifications = _boxNotifications =
await Hive.openBox<NotificationModel>(boxNameNotifications); await Hive.openBox<NotificationModel>(boxNameNotifications);
_boxWatchedTransactions = _boxWatchedTransactions =
@ -116,10 +145,15 @@ class DB {
name, WalletInfo.fromJson(Map<String, dynamic>.from(dyn as Map)))); name, WalletInfo.fromJson(Map<String, dynamic>.from(dyn as Map))));
for (final entry in mapped.entries) { for (final entry in mapped.entries) {
if (Hive.isBoxOpen(entry.value.walletId)) {
_walletBoxes[entry.value.walletId] =
Hive.box<dynamic>(entry.value.walletId);
} else {
_walletBoxes[entry.value.walletId] = _walletBoxes[entry.value.walletId] =
await Hive.openBox<dynamic>(entry.value.walletId); await Hive.openBox<dynamic>(entry.value.walletId);
} }
} }
}
Future<void> _loadSharedCoinCacheBoxes() async { Future<void> _loadSharedCoinCacheBoxes() async {
for (final coin in Coin.values) { for (final coin in Coin.values) {

View file

@ -51,6 +51,7 @@ import 'package:stackwallet/services/trade_service.dart';
import 'package:stackwallet/services/wallets.dart'; import 'package:stackwallet/services/wallets.dart';
import 'package:stackwallet/utilities/cfcolors.dart'; import 'package:stackwallet/utilities/cfcolors.dart';
import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/db_version_migration.dart';
import 'package:stackwallet/utilities/enums/backup_frequency_type.dart'; import 'package:stackwallet/utilities/enums/backup_frequency_type.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';
@ -119,18 +120,16 @@ void main() async {
Hive.registerAdapter(UnspentCoinsInfoAdapter()); Hive.registerAdapter(UnspentCoinsInfoAdapter());
await Hive.openBox<dynamic>(DB.boxNameDBInfo);
int dbVersion = DB.instance.get<dynamic>(
boxName: DB.boxNameDBInfo, key: "hive_data_version") as int? ??
0;
if (dbVersion < Constants.currentHiveDbVersion) {
await DbVersionMigrator().migrate(dbVersion);
}
monero.onStartup(); monero.onStartup();
// final wallets = await Hive.openBox('wallets');
// await wallets.put('currentWalletName', "");
// NOT USED YET
// int dbVersion = await wallets.get("db_version");
// if (dbVersion == null || dbVersion < Constants.currentDbVersion) {
// if (dbVersion == null) dbVersion = 0;
// await DbVersionMigrator().migrate(dbVersion);
// }
// SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, // SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
// overlays: [SystemUiOverlay.bottom]); // overlays: [SystemUiOverlay.bottom]);
await NotificationApi.init(); await NotificationApi.init();
@ -344,11 +343,13 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
_prefs = ref.read(prefsChangeNotifierProvider); _prefs = ref.read(prefsChangeNotifierProvider);
_wallets = ref.read(walletsChangeNotifierProvider); _wallets = ref.read(walletsChangeNotifierProvider);
if (Platform.isAndroid) {
WidgetsBinding.instance.addPostFrameCallback((_) async { WidgetsBinding.instance.addPostFrameCallback((_) async {
// fetch open file if it exists // fetch open file if it exists
await getOpenFile(); await getOpenFile();
if (ref.read(openedFromSWBFileStringStateProvider.state).state != null) { if (ref.read(openedFromSWBFileStringStateProvider.state).state !=
null) {
// waiting for loading to complete before going straight to restore if the app was opened via file // waiting for loading to complete before going straight to restore if the app was opened via file
await loadingCompleter.future; await loadingCompleter.future;
@ -358,6 +359,7 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
} }
// ref.read(shouldShowLockscreenOnResumeStateProvider.state).state = false; // ref.read(shouldShowLockscreenOnResumeStateProvider.state).state = false;
}); });
}
super.initState(); super.initState();
} }
@ -378,6 +380,7 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
case AppLifecycleState.paused: case AppLifecycleState.paused:
break; break;
case AppLifecycleState.resumed: case AppLifecycleState.resumed:
if (Platform.isAndroid) {
// fetch open file if it exists // fetch open file if it exists
await getOpenFile(); await getOpenFile();
// go straight to restore if the app was resumed via file // go straight to restore if the app was resumed via file
@ -387,6 +390,7 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
ref.read(openedFromSWBFileStringStateProvider.state).state!); ref.read(openedFromSWBFileStringStateProvider.state).state!);
ref.read(openedFromSWBFileStringStateProvider.state).state = null; ref.read(openedFromSWBFileStringStateProvider.state).state = null;
} }
}
// if (ref.read(hasAuthenticatedOnStartStateProvider.state).state && // if (ref.read(hasAuthenticatedOnStartStateProvider.state).state &&
// ref.read(shouldShowLockscreenOnResumeStateProvider.state).state) { // ref.read(shouldShowLockscreenOnResumeStateProvider.state).state) {
// final now = DateTime.now().toUtc().millisecondsSinceEpoch ~/ 1000; // final now = DateTime.now().toUtc().millisecondsSinceEpoch ~/ 1000;
@ -419,6 +423,7 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
} }
} }
/// should only be called on android currently
Future<void> getOpenFile() async { Future<void> getOpenFile() async {
// update provider with new file content state // update provider with new file content state
ref.read(openedFromSWBFileStringStateProvider.state).state = ref.read(openedFromSWBFileStringStateProvider.state).state =
@ -432,6 +437,7 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
level: LogLevel.Info); level: LogLevel.Info);
} }
/// should only be called on android currently
Future<void> resetOpenPath() async { Future<void> resetOpenPath() async {
await platform.invokeMethod("resetOpenPath"); await platform.invokeMethod("resetOpenPath");
} }

View file

@ -1,10 +1,14 @@
import 'package:decimal/decimal.dart'; import 'package:decimal/decimal.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:stackwallet/models/exchange/change_now/currency.dart'; import 'package:stackwallet/models/exchange/change_now/currency.dart';
import 'package:stackwallet/services/change_now/change_now.dart'; import 'package:stackwallet/services/change_now/change_now.dart';
import 'package:stackwallet/utilities/logger.dart'; import 'package:stackwallet/utilities/logger.dart';
class EstimatedRateExchangeFormState extends ChangeNotifier { class EstimatedRateExchangeFormState extends ChangeNotifier {
/// used in testing to inject mock
ChangeNow? cnTesting;
Decimal? _fromAmount; Decimal? _fromAmount;
Decimal? _toAmount; Decimal? _toAmount;
@ -16,9 +20,43 @@ class EstimatedRateExchangeFormState extends ChangeNotifier {
Currency? _from; Currency? _from;
Currency? _to; Currency? _to;
void Function(String)? _onError;
Currency? get from => _from; Currency? get from => _from;
Currency? get to => _to; Currency? get to => _to;
String get fromAmountString =>
_fromAmount == null ? "" : _fromAmount!.toStringAsFixed(8);
String get toAmountString =>
_toAmount == null ? "" : _toAmount!.toStringAsFixed(8);
String get rateDisplayString {
if (rate == null || from == null || to == null) {
return "N/A";
} else {
return "1 ${from!.ticker.toUpperCase()} ~${rate!.toStringAsFixed(8)} ${to!.ticker.toUpperCase()}";
}
}
bool get canExchange {
return _fromAmount != null &&
_fromAmount != Decimal.zero &&
_toAmount != null &&
rate != null &&
minimumSendWarning.isEmpty;
}
String get minimumSendWarning {
if (_from != null &&
_fromAmount != null &&
_minFromAmount != null &&
_fromAmount! < _minFromAmount!) {
return "Minimum amount ${_minFromAmount!.toString()} ${from!.ticker.toUpperCase()}";
}
return "";
}
Future<void> init(Currency? from, Currency? to) async { Future<void> init(Currency? from, Currency? to) async {
_from = from; _from = from;
_to = to; _to = to;
@ -43,10 +81,6 @@ class EstimatedRateExchangeFormState extends ChangeNotifier {
final Decimal? newMinFromAmount = _minToAmount; final Decimal? newMinFromAmount = _minToAmount;
final Decimal? newMinToAmount = _minFromAmount; final Decimal? newMinToAmount = _minFromAmount;
// final Decimal? newRate = rate == null
// ? rate
// : (Decimal.one / rate!).toDecimal(scaleOnInfinitePrecision: 12);
final Currency? newTo = from; final Currency? newTo = from;
final Currency? newFrom = to; final Currency? newFrom = to;
@ -63,48 +97,11 @@ class EstimatedRateExchangeFormState extends ChangeNotifier {
await _updateMinFromAmount(shouldNotifyListeners: false); await _updateMinFromAmount(shouldNotifyListeners: false);
rate = null; await updateRate();
if (_fromAmount != null) {
Decimal? amt;
if (_minFromAmount != null) {
if (_minFromAmount! > _fromAmount!) {
amt = await getStandardEstimatedToAmount(
fromAmount: _minFromAmount!, from: _from!, to: _to!);
if (amt != null) {
rate =
(amt / _minFromAmount!).toDecimal(scaleOnInfinitePrecision: 12);
}
} else {
amt = await getStandardEstimatedToAmount(
fromAmount: _fromAmount!, from: _from!, to: _to!);
if (amt != null) {
rate = (amt / _fromAmount!).toDecimal(scaleOnInfinitePrecision: 12);
}
}
}
if (rate != null) {
_toAmount = (_fromAmount! * rate!);
}
} else {
if (_minFromAmount != null) {
Decimal? amt = await getStandardEstimatedToAmount(
fromAmount: _minFromAmount!, from: _from!, to: _to!);
if (amt != null) {
rate =
(amt / _minFromAmount!).toDecimal(scaleOnInfinitePrecision: 12);
}
}
}
notifyListeners(); notifyListeners();
} }
String get fromAmountString =>
_fromAmount == null ? "" : _fromAmount!.toStringAsFixed(8);
String get toAmountString =>
_toAmount == null ? "" : _toAmount!.toStringAsFixed(8);
Future<void> updateTo(Currency to, bool shouldNotifyListeners) async { Future<void> updateTo(Currency to, bool shouldNotifyListeners) async {
try { try {
_to = to; _to = to;
@ -115,46 +112,8 @@ class EstimatedRateExchangeFormState extends ChangeNotifier {
} }
await _updateMinFromAmount(shouldNotifyListeners: shouldNotifyListeners); await _updateMinFromAmount(shouldNotifyListeners: shouldNotifyListeners);
// await _updateMinToAmount(shouldNotifyListeners: shouldNotifyListeners);
rate = null; await updateRate();
if (_fromAmount != null) {
Decimal? amt;
if (_minFromAmount != null) {
if (_minFromAmount! > _fromAmount!) {
amt = await getStandardEstimatedToAmount(
fromAmount: _minFromAmount!, from: _from!, to: _to!);
if (amt != null) {
rate = (amt / _minFromAmount!)
.toDecimal(scaleOnInfinitePrecision: 12);
}
debugPrint("A");
} else {
amt = await getStandardEstimatedToAmount(
fromAmount: _fromAmount!, from: _from!, to: _to!);
if (amt != null) {
rate =
(amt / _fromAmount!).toDecimal(scaleOnInfinitePrecision: 12);
}
debugPrint("B");
}
}
if (rate != null) {
_toAmount = (_fromAmount! * rate!);
}
debugPrint("C");
} else {
if (_minFromAmount != null) {
Decimal? amt = await getStandardEstimatedToAmount(
fromAmount: _minFromAmount!, from: _from!, to: _to!);
if (amt != null) {
rate =
(amt / _minFromAmount!).toDecimal(scaleOnInfinitePrecision: 12);
}
debugPrint("D");
}
}
debugPrint( debugPrint(
"_updated TO: _from=${_from!.ticker} _to=${_to!.ticker} _fromAmount=$_fromAmount _toAmount=$_toAmount rate:$rate"); "_updated TO: _from=${_from!.ticker} _to=${_to!.ticker} _fromAmount=$_fromAmount _toAmount=$_toAmount rate:$rate");
@ -163,7 +122,7 @@ class EstimatedRateExchangeFormState extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
} catch (e, s) { } catch (e, s) {
Logging.instance.log("$e\n$s", level: LogLevel.Fatal); Logging.instance.log("$e\n$s", level: LogLevel.Error);
} }
} }
@ -179,40 +138,7 @@ class EstimatedRateExchangeFormState extends ChangeNotifier {
await _updateMinFromAmount(shouldNotifyListeners: shouldNotifyListeners); await _updateMinFromAmount(shouldNotifyListeners: shouldNotifyListeners);
rate = null; await updateRate();
if (_fromAmount != null) {
Decimal? amt;
if (_minFromAmount != null) {
if (_minFromAmount! > _fromAmount!) {
amt = await getStandardEstimatedToAmount(
fromAmount: _minFromAmount!, from: _from!, to: _to!);
if (amt != null) {
rate = (amt / _minFromAmount!)
.toDecimal(scaleOnInfinitePrecision: 12);
}
} else {
amt = await getStandardEstimatedToAmount(
fromAmount: _fromAmount!, from: _from!, to: _to!);
if (amt != null) {
rate =
(amt / _fromAmount!).toDecimal(scaleOnInfinitePrecision: 12);
}
}
}
if (rate != null) {
_toAmount = (_fromAmount! * rate!);
}
} else {
if (_minFromAmount != null) {
Decimal? amt = await getStandardEstimatedToAmount(
fromAmount: _minFromAmount!, from: _from!, to: _to!);
if (amt != null) {
rate =
(amt / _minFromAmount!).toDecimal(scaleOnInfinitePrecision: 12);
}
}
}
debugPrint( debugPrint(
"_updated FROM: _from=${_from!.ticker} _to=${_to!.ticker} _fromAmount=$_fromAmount _toAmount=$_toAmount rate:$rate"); "_updated FROM: _from=${_from!.ticker} _to=${_to!.ticker} _fromAmount=$_fromAmount _toAmount=$_toAmount rate:$rate");
@ -220,55 +146,10 @@ class EstimatedRateExchangeFormState extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
} catch (e, s) { } catch (e, s) {
Logging.instance.log("$e\n$s", level: LogLevel.Fatal); Logging.instance.log("$e\n$s", level: LogLevel.Error);
} }
} }
String get rateDisplayString {
if (rate == null || from == null || to == null) {
return "N/A";
} else {
return "1 ${from!.ticker.toUpperCase()} ~${rate!.toStringAsFixed(8)} ${to!.ticker.toUpperCase()}";
}
}
bool get canExchange {
return _fromAmount != null &&
_fromAmount != Decimal.zero &&
_toAmount != null &&
rate != null &&
minimumReceiveWarning.isEmpty &&
minimumSendWarning.isEmpty;
}
String get minimumSendWarning {
if (_from != null &&
_fromAmount != null &&
_minFromAmount != null &&
_fromAmount! < _minFromAmount!) {
return "Minimum amount ${_minFromAmount!.toString()} ${from!.ticker.toUpperCase()}";
}
return "";
}
String get minimumReceiveWarning {
// TODO not sure this is needed
// if (_toAmount != null &&
// _minToAmount != null &&
// _toAmount! < _minToAmount!) {
// return "Minimum amount ${_minToAmount!.toString()} ${to.ticker.toUpperCase()}";
// }
return "";
}
// Future<void> _updateMinToAmount({required bool shouldNotifyListeners}) async {
// _minToAmount = await getStandardMinExchangeAmount(from: to!, to: from!);
// if (shouldNotifyListeners) {
// notifyListeners();
// }
// }
Future<void> _updateMinFromAmount( Future<void> _updateMinFromAmount(
{required bool shouldNotifyListeners}) async { {required bool shouldNotifyListeners}) async {
_minFromAmount = await getStandardMinExchangeAmount(from: from!, to: to!); _minFromAmount = await getStandardMinExchangeAmount(from: from!, to: to!);
@ -277,48 +158,32 @@ class EstimatedRateExchangeFormState extends ChangeNotifier {
} }
} }
Future<void> setToAmountAndCalculateFromAmount( // Future<void> setToAmountAndCalculateFromAmount(
Decimal newToAmount, // Decimal newToAmount,
bool shouldNotifyListeners, // bool shouldNotifyListeners,
) async { // ) async {
// if (newToAmount == Decimal.zero) { // if (newToAmount == Decimal.zero) {
// _fromAmount = Decimal.zero; // _fromAmount = Decimal.zero;
// _toAmount = Decimal.zero; // }
//
// _toAmount = newToAmount;
// await updateRate();
// if (shouldNotifyListeners) { // if (shouldNotifyListeners) {
// notifyListeners(); // notifyListeners();
// } // }
// return;
// } // }
if (rate != null) {
_fromAmount =
(newToAmount / rate!).toDecimal(scaleOnInfinitePrecision: 12);
}
_toAmount = newToAmount;
if (shouldNotifyListeners) {
notifyListeners();
}
}
Future<void> setFromAmountAndCalculateToAmount( Future<void> setFromAmountAndCalculateToAmount(
Decimal newFromAmount, Decimal newFromAmount,
bool shouldNotifyListeners, bool shouldNotifyListeners,
) async { ) async {
// if (newFromAmount == Decimal.zero) { if (newFromAmount == Decimal.zero) {
// _fromAmount = Decimal.zero; _toAmount = Decimal.zero;
// _toAmount = Decimal.zero;
// if (shouldNotifyListeners) {
// notifyListeners();
// }
// return;
// }
if (rate != null) {
_toAmount = (newFromAmount * rate!);
} }
_fromAmount = newFromAmount; _fromAmount = newFromAmount;
await updateRate();
if (shouldNotifyListeners) { if (shouldNotifyListeners) {
notifyListeners(); notifyListeners();
} }
@ -329,8 +194,12 @@ class EstimatedRateExchangeFormState extends ChangeNotifier {
required Currency from, required Currency from,
required Currency to, required Currency to,
}) async { }) async {
final response = await ChangeNow.instance.getEstimatedExchangeAmount( final response =
fromTicker: from.ticker, toTicker: to.ticker, fromAmount: fromAmount); await (cnTesting ?? ChangeNow.instance).getEstimatedExchangeAmount(
fromTicker: from.ticker,
toTicker: to.ticker,
fromAmount: fromAmount,
);
if (response.value != null) { if (response.value != null) {
return response.value!.estimatedAmount; return response.value!.estimatedAmount;
@ -341,11 +210,31 @@ class EstimatedRateExchangeFormState extends ChangeNotifier {
} }
} }
// Future<Decimal?> getStandardEstimatedFromAmount({
// required Decimal toAmount,
// required Currency from,
// required Currency to,
// }) async {
// final response = await (cnTesting ?? ChangeNow.instance)
// .getEstimatedExchangeAmount(
// fromTicker: from.ticker,
// toTicker: to.ticker,
// fromAmount: toAmount, );
//
// if (response.value != null) {
// return response.value!.fromAmount;
// } else {
// _onError?.call(
// "Failed to fetch estimated amount: ${response.exception?.toString()}");
// return null;
// }
// }
Future<Decimal?> getStandardMinExchangeAmount({ Future<Decimal?> getStandardMinExchangeAmount({
required Currency from, required Currency from,
required Currency to, required Currency to,
}) async { }) async {
final response = await ChangeNow.instance final response = await (cnTesting ?? ChangeNow.instance)
.getMinimalExchangeAmount(fromTicker: from.ticker, toTicker: to.ticker); .getMinimalExchangeAmount(fromTicker: from.ticker, toTicker: to.ticker);
if (response.value != null) { if (response.value != null) {
@ -357,8 +246,6 @@ class EstimatedRateExchangeFormState extends ChangeNotifier {
} }
} }
void Function(String)? _onError;
void setOnError({ void setOnError({
required void Function(String)? onError, required void Function(String)? onError,
bool shouldNotifyListeners = false, bool shouldNotifyListeners = false,
@ -368,4 +255,25 @@ class EstimatedRateExchangeFormState extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
} }
Future<void> updateRate() async {
rate = null;
final amount = _fromAmount;
final minAmount = _minFromAmount;
if (amount != null && amount > Decimal.zero) {
Decimal? amt;
if (minAmount != null) {
if (minAmount <= amount) {
amt = await getStandardEstimatedToAmount(
fromAmount: amount, from: _from!, to: _to!);
if (amt != null) {
rate = (amt / amount).toDecimal(scaleOnInfinitePrecision: 12);
}
}
}
if (rate != null && amt != null) {
_toAmount = amt;
}
}
}
} }

View file

@ -164,7 +164,7 @@ class _Step4ViewState extends ConsumerState<Step4View> {
height: 8, height: 8,
), ),
Text( Text(
"Send FIRO to the address below. Once it is received, ChangeNOW will send the BTC to the recipient address you provided. You can find this trade details and check its status in the list of trades.", "Send ${model.sendTicker} to the address below. Once it is received, ChangeNOW will send the ${model.receiveTicker} to the recipient address you provided. You can find this trade details and check its status in the list of trades.",
style: STextStyles.itemSubtitle, style: STextStyles.itemSubtitle,
), ),
const SizedBox( const SizedBox(

View file

@ -231,6 +231,65 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
? ref.read(estimatedRateExchangeFormProvider).toAmountString ? ref.read(estimatedRateExchangeFormProvider).toAmountString
: ref.read(fixedRateExchangeFormProvider).toAmountString; : ref.read(fixedRateExchangeFormProvider).toAmountString;
_sendFocusNode.addListener(() async {
if (!_sendFocusNode.hasFocus) {
final newFromAmount = Decimal.tryParse(_sendController.text);
if (newFromAmount != null) {
if (ref.read(prefsChangeNotifierProvider).exchangeRateType ==
ExchangeRateType.estimated) {
await ref
.read(estimatedRateExchangeFormProvider)
.setFromAmountAndCalculateToAmount(newFromAmount, true);
} else {
await ref
.read(fixedRateExchangeFormProvider)
.setFromAmountAndCalculateToAmount(newFromAmount, true);
}
} else {
if (ref.read(prefsChangeNotifierProvider).exchangeRateType ==
ExchangeRateType.estimated) {
await ref
.read(estimatedRateExchangeFormProvider)
.setFromAmountAndCalculateToAmount(Decimal.zero, true);
} else {
await ref
.read(fixedRateExchangeFormProvider)
.setFromAmountAndCalculateToAmount(Decimal.zero, true);
}
_receiveController.text = "";
}
}
});
_receiveFocusNode.addListener(() async {
if (!_receiveFocusNode.hasFocus) {
final newToAmount = Decimal.tryParse(_receiveController.text);
if (newToAmount != null) {
if (ref.read(prefsChangeNotifierProvider).exchangeRateType ==
ExchangeRateType.estimated) {
// await ref
// .read(estimatedRateExchangeFormProvider)
// .setToAmountAndCalculateFromAmount(newToAmount, true);
} else {
await ref
.read(fixedRateExchangeFormProvider)
.setToAmountAndCalculateFromAmount(newToAmount, true);
}
} else {
if (ref.read(prefsChangeNotifierProvider).exchangeRateType ==
ExchangeRateType.estimated) {
// await ref
// .read(estimatedRateExchangeFormProvider)
// .setToAmountAndCalculateFromAmount(Decimal.zero, true);
} else {
await ref
.read(fixedRateExchangeFormProvider)
.setToAmountAndCalculateFromAmount(Decimal.zero, true);
}
_sendController.text = "";
}
}
});
super.initState(); super.initState();
} }
@ -332,12 +391,12 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
await ref await ref
.read(estimatedRateExchangeFormProvider) .read(estimatedRateExchangeFormProvider)
.setFromAmountAndCalculateToAmount( .setFromAmountAndCalculateToAmount(
newFromAmount, true); newFromAmount, false);
} else { } else {
await ref await ref
.read(fixedRateExchangeFormProvider) .read(fixedRateExchangeFormProvider)
.setFromAmountAndCalculateToAmount( .setFromAmountAndCalculateToAmount(
newFromAmount, true); newFromAmount, false);
} }
} else { } else {
if (ref if (ref
@ -347,12 +406,12 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
await ref await ref
.read(estimatedRateExchangeFormProvider) .read(estimatedRateExchangeFormProvider)
.setFromAmountAndCalculateToAmount( .setFromAmountAndCalculateToAmount(
Decimal.zero, true); Decimal.zero, false);
} else { } else {
await ref await ref
.read(fixedRateExchangeFormProvider) .read(fixedRateExchangeFormProvider)
.setFromAmountAndCalculateToAmount( .setFromAmountAndCalculateToAmount(
Decimal.zero, true); Decimal.zero, false);
} }
_receiveController.text = ""; _receiveController.text = "";
} }
@ -631,6 +690,10 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
TextFormField( TextFormField(
focusNode: _receiveFocusNode, focusNode: _receiveFocusNode,
controller: _receiveController, controller: _receiveController,
readOnly: ref
.read(prefsChangeNotifierProvider)
.exchangeRateType ==
ExchangeRateType.estimated,
onTap: () { onTap: () {
if (_receiveController.text == "-") { if (_receiveController.text == "-") {
_receiveController.text = ""; _receiveController.text = "";
@ -643,30 +706,30 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
.read(prefsChangeNotifierProvider) .read(prefsChangeNotifierProvider)
.exchangeRateType == .exchangeRateType ==
ExchangeRateType.estimated) { ExchangeRateType.estimated) {
await ref // await ref
.read(estimatedRateExchangeFormProvider) // .read(estimatedRateExchangeFormProvider)
.setToAmountAndCalculateFromAmount( // .setToAmountAndCalculateFromAmount(
newToAmount, true); // newToAmount, false);
} else { } else {
await ref await ref
.read(fixedRateExchangeFormProvider) .read(fixedRateExchangeFormProvider)
.setToAmountAndCalculateFromAmount( .setToAmountAndCalculateFromAmount(
newToAmount, true); newToAmount, false);
} }
} else { } else {
if (ref if (ref
.read(prefsChangeNotifierProvider) .read(prefsChangeNotifierProvider)
.exchangeRateType == .exchangeRateType ==
ExchangeRateType.estimated) { ExchangeRateType.estimated) {
await ref // await ref
.read(estimatedRateExchangeFormProvider) // .read(estimatedRateExchangeFormProvider)
.setToAmountAndCalculateFromAmount( // .setToAmountAndCalculateFromAmount(
Decimal.zero, true); // Decimal.zero, false);
} else { } else {
await ref await ref
.read(fixedRateExchangeFormProvider) .read(fixedRateExchangeFormProvider)
.setToAmountAndCalculateFromAmount( .setToAmountAndCalculateFromAmount(
Decimal.zero, true); Decimal.zero, false);
} }
_sendController.text = ""; _sendController.text = "";
} }

View file

@ -55,7 +55,7 @@ class StepRow extends StatelessWidget {
)); ));
} }
list.add(StepIndicator( list.add(StepIndicator(
step: count - 1, step: count,
status: getStatus(count - 1), status: getStatus(count - 1),
)); ));
return list; return list;

View file

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:decimal/decimal.dart'; import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@ -10,6 +12,7 @@ import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/exchange_view/edit_trade_note_view.dart'; import 'package:stackwallet/pages/exchange_view/edit_trade_note_view.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/edit_note_view.dart'; import 'package:stackwallet/pages/wallet_view/transaction_views/edit_note_view.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_details_view.dart'; import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_details_view.dart';
import 'package:stackwallet/providers/exchange/change_now_provider.dart';
import 'package:stackwallet/providers/exchange/trade_note_service_provider.dart'; import 'package:stackwallet/providers/exchange/trade_note_service_provider.dart';
import 'package:stackwallet/providers/global/trades_service_provider.dart'; import 'package:stackwallet/providers/global/trades_service_provider.dart';
import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/providers/providers.dart';
@ -63,6 +66,26 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
clipboard = widget.clipboard; clipboard = widget.clipboard;
transactionIfSentFromStack = widget.transactionIfSentFromStack; transactionIfSentFromStack = widget.transactionIfSentFromStack;
walletId = widget.walletId; walletId = widget.walletId;
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
final trade = ref
.read(tradesServiceProvider)
.trades
.firstWhere((e) => e.id == tradeId);
if (mounted && trade.statusObject == null ||
trade.statusObject!.amountSendDecimal.isEmpty) {
final status = await ref
.read(changeNowProvider)
.getTransactionStatus(id: trade.id);
if (mounted && status.value != null) {
await ref.read(tradesServiceProvider).edit(
trade: trade.copyWith(statusObject: status.value),
shouldNotifyListeners: true);
}
}
});
super.initState(); super.initState();
} }
@ -77,8 +100,6 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
status = ChangeNowTransactionStatus.Failed; status = ChangeNowTransactionStatus.Failed;
} }
debugPrint("statusstatusstatusstatus: $status");
debugPrint("statusstatusstatusstatusSTRING: $statusString");
switch (status) { switch (status) {
case ChangeNowTransactionStatus.New: case ChangeNowTransactionStatus.New:
case ChangeNowTransactionStatus.Waiting: case ChangeNowTransactionStatus.Waiting:
@ -113,6 +134,11 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
debugPrint("hasTx: $hasTx"); debugPrint("hasTx: $hasTx");
debugPrint("trade: ${trade.toString()}"); debugPrint("trade: ${trade.toString()}");
final sendAmount = Decimal.tryParse(
trade.statusObject?.amountSendDecimal ?? "") ??
Decimal.tryParse(trade.statusObject?.expectedSendAmountDecimal ?? "") ??
Decimal.parse("-1");
return Scaffold( return Scaffold(
backgroundColor: CFColors.almostWhite, backgroundColor: CFColors.almostWhite,
appBar: AppBar( appBar: AppBar(
@ -150,7 +176,7 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
height: 4, height: 4,
), ),
SelectableText( SelectableText(
"${Format.localizedStringAsFixed(value: Decimal.parse(trade.statusObject?.amountSendDecimal ?? trade.amount), locale: ref.watch( "${Format.localizedStringAsFixed(value: sendAmount, locale: ref.watch(
localeServiceChangeNotifierProvider localeServiceChangeNotifierProvider
.select((value) => value.locale), .select((value) => value.locale),
), decimalPlaces: trade.fromCurrency.toLowerCase() == "xmr" ? 12 : 8)} ${trade.fromCurrency.toUpperCase()}", ), decimalPlaces: trade.fromCurrency.toLowerCase() == "xmr" ? 12 : 8)} ${trade.fromCurrency.toUpperCase()}",
@ -205,7 +231,7 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
], ],
), ),
), ),
if (!sentFromStack && hasTx) if (!sentFromStack && !hasTx)
const SizedBox( const SizedBox(
height: 12, height: 12,
), ),
@ -214,9 +240,8 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
color: CFColors.warningBackground, color: CFColors.warningBackground,
child: RichText( child: RichText(
text: TextSpan( text: TextSpan(
text: "You must send at least ${Decimal.parse( text:
trade.statusObject!.amountSendDecimal, "You must send at least ${sendAmount.toStringAsFixed(
).toStringAsFixed(
trade.fromCurrency.toLowerCase() == "xmr" ? 12 : 8, trade.fromCurrency.toLowerCase() == "xmr" ? 12 : 8,
)} ${trade.fromCurrency.toUpperCase()}. ", )} ${trade.fromCurrency.toUpperCase()}. ",
style: STextStyles.label.copyWith( style: STextStyles.label.copyWith(
@ -225,9 +250,8 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
), ),
children: [ children: [
TextSpan( TextSpan(
text: "If you send less than ${Decimal.parse( text:
trade.statusObject!.amountSendDecimal, "If you send less than ${sendAmount.toStringAsFixed(
).toStringAsFixed(
trade.fromCurrency.toLowerCase() == "xmr" trade.fromCurrency.toLowerCase() == "xmr"
? 12 ? 12
: 8, : 8,
@ -623,11 +647,11 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
onTap: () async { onTap: () async {
final data = ClipboardData(text: trade.id); final data = ClipboardData(text: trade.id);
await clipboard.setData(data); await clipboard.setData(data);
showFloatingFlushBar( unawaited(showFloatingFlushBar(
type: FlushBarType.info, type: FlushBarType.info,
message: "Copied to clipboard", message: "Copied to clipboard",
context: context, context: context,
); ));
}, },
child: SvgPicture.asset( child: SvgPicture.asset(
Assets.svg.copy, Assets.svg.copy,

View file

@ -716,6 +716,10 @@ class _WalletInitiatedExchangeViewState
TextFormField( TextFormField(
focusNode: _receiveFocusNode, focusNode: _receiveFocusNode,
controller: _receiveController, controller: _receiveController,
readOnly: ref
.read(prefsChangeNotifierProvider)
.exchangeRateType ==
ExchangeRateType.estimated,
onTap: () { onTap: () {
if (_receiveController.text == "-") { if (_receiveController.text == "-") {
_receiveController.text = ""; _receiveController.text = "";
@ -728,10 +732,10 @@ class _WalletInitiatedExchangeViewState
.read(prefsChangeNotifierProvider) .read(prefsChangeNotifierProvider)
.exchangeRateType == .exchangeRateType ==
ExchangeRateType.estimated) { ExchangeRateType.estimated) {
await ref // await ref
.read(estimatedRateExchangeFormProvider) // .read(estimatedRateExchangeFormProvider)
.setToAmountAndCalculateFromAmount( // .setToAmountAndCalculateFromAmount(
newToAmount, true); // newToAmount, true);
} else { } else {
await ref await ref
.read(fixedRateExchangeFormProvider) .read(fixedRateExchangeFormProvider)
@ -743,10 +747,10 @@ class _WalletInitiatedExchangeViewState
.read(prefsChangeNotifierProvider) .read(prefsChangeNotifierProvider)
.exchangeRateType == .exchangeRateType ==
ExchangeRateType.estimated) { ExchangeRateType.estimated) {
await ref // await ref
.read(estimatedRateExchangeFormProvider) // .read(estimatedRateExchangeFormProvider)
.setToAmountAndCalculateFromAmount( // .setToAmountAndCalculateFromAmount(
Decimal.zero, true); // Decimal.zero, true);
} else { } else {
await ref await ref
.read(fixedRateExchangeFormProvider) .read(fixedRateExchangeFormProvider)

View file

@ -6,10 +6,12 @@ import 'dart:ui' as ui;
import 'package:decimal/decimal.dart'; import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter_svg/svg.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:qr_flutter/qr_flutter.dart'; import 'package:qr_flutter/qr_flutter.dart';
import 'package:share_plus/share_plus.dart'; import 'package:share_plus/share_plus.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart'; import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/cfcolors.dart'; import 'package:stackwallet/utilities/cfcolors.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart'; import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/constants.dart';
@ -318,8 +320,7 @@ class _GenerateUriQrCodeViewState extends State<GenerateUriQrCodeView> {
child: QrImage( child: QrImage(
data: uriString, data: uriString,
size: width, size: width,
backgroundColor: backgroundColor: CFColors.white,
CFColors.almostWhite,
foregroundColor: foregroundColor:
CFColors.stackAccent, CFColors.stackAccent,
), ),
@ -344,13 +345,41 @@ class _GenerateUriQrCodeViewState extends State<GenerateUriQrCodeView> {
CFColors.buttonGray, CFColors.buttonGray,
), ),
), ),
child: Text( child: Row(
"Share", mainAxisAlignment:
style: MainAxisAlignment.center,
STextStyles.button.copyWith( crossAxisAlignment:
color: CFColors.stackAccent, CrossAxisAlignment.center,
children: [
Center(
child: SvgPicture.asset(
Assets.svg.share,
width: 14,
height: 14,
), ),
), ),
const SizedBox(
width: 4,
),
Column(
children: [
Text(
"Share",
textAlign:
TextAlign.center,
style: STextStyles.button
.copyWith(
color: CFColors
.stackAccent,
),
),
const SizedBox(
height: 2,
),
],
),
],
),
), ),
), ),
), ),

View file

@ -1,36 +1,114 @@
import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:qr_flutter/qr_flutter.dart'; import 'package:qr_flutter/qr_flutter.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart'; import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/receive_view/generate_receiving_uri_qr_code_view.dart'; import 'package:stackwallet/pages/receive_view/generate_receiving_uri_qr_code_view.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/route_generator.dart'; import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/cfcolors.dart'; import 'package:stackwallet/utilities/cfcolors.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart'; import 'package:stackwallet/utilities/clipboard_interface.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/enums/flush_bar_type.dart'; import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
import 'package:stackwallet/widgets/custom_loading_overlay.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
class ReceiveView extends StatelessWidget { class ReceiveView extends ConsumerStatefulWidget {
const ReceiveView({ const ReceiveView({
Key? key, Key? key,
required this.coin, required this.coin,
required this.receivingAddress, required this.walletId,
this.clipboard = const ClipboardWrapper(), this.clipboard = const ClipboardWrapper(),
}) : super(key: key); }) : super(key: key);
static const String routeName = "/receiveView"; static const String routeName = "/receiveView";
final Coin coin; final Coin coin;
final String receivingAddress; final String walletId;
final ClipboardInterface clipboard; final ClipboardInterface clipboard;
@override
ConsumerState<ReceiveView> createState() => _ReceiveViewState();
}
class _ReceiveViewState extends ConsumerState<ReceiveView> {
late final Coin coin;
late final String walletId;
late final ClipboardInterface clipboard;
Future<void> generateNewAddress() async {
bool shouldPop = false;
unawaited(
showDialog(
context: context,
builder: (_) {
return WillPopScope(
onWillPop: () async => shouldPop,
child: const CustomLoadingOverlay(
message: "Generating address",
eventBus: null,
),
);
},
),
);
await ref
.read(walletsChangeNotifierProvider)
.getManager(walletId)
.generateNewAddress();
shouldPop = true;
if (mounted) {
Navigator.of(context)
.popUntil(ModalRoute.withName(ReceiveView.routeName));
}
}
String receivingAddress = "";
@override
void initState() {
walletId = widget.walletId;
coin = widget.coin;
clipboard = widget.clipboard;
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
final address = await ref
.read(walletsChangeNotifierProvider)
.getManager(walletId)
.currentReceivingAddress;
setState(() {
receivingAddress = address;
});
});
super.initState();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType"); debugPrint("BUILD: $runtimeType");
ref.listen(
ref
.read(walletsChangeNotifierProvider)
.getManagerProvider(walletId)
.select((value) => value.currentReceivingAddress),
(previous, next) {
if (next is Future<String>) {
next.then((value) => setState(() => receivingAddress = value));
}
});
return Scaffold( return Scaffold(
backgroundColor: CFColors.almostWhite, backgroundColor: CFColors.almostWhite,
appBar: AppBar( appBar: AppBar(
@ -52,24 +130,6 @@ class ReceiveView extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
Container(
decoration: BoxDecoration(
color: CFColors.white,
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
children: [
Row(
children: [
Text(
"Your ${coin.ticker} address",
style: STextStyles.itemSubtitle,
),
const Spacer(),
GestureDetector( GestureDetector(
onTap: () { onTap: () {
clipboard.setData( clipboard.setData(
@ -82,7 +142,17 @@ class ReceiveView extends StatelessWidget {
context: context, context: context,
); );
}, },
child: Row( child: RoundedWhiteContainer(
child: Column(
children: [
Row(
children: [
Text(
"Your ${coin.ticker} address",
style: STextStyles.itemSubtitle,
),
const Spacer(),
Row(
children: [ children: [
SvgPicture.asset( SvgPicture.asset(
Assets.svg.copy, Assets.svg.copy,
@ -99,7 +169,6 @@ class ReceiveView extends StatelessWidget {
), ),
], ],
), ),
),
], ],
), ),
const SizedBox( const SizedBox(
@ -119,25 +188,46 @@ class ReceiveView extends StatelessWidget {
), ),
), ),
), ),
if (coin != Coin.epicCash)
const SizedBox(
height: 12,
),
if (coin != Coin.epicCash)
TextButton(
onPressed: generateNewAddress,
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(
CFColors.buttonGray,
),
),
child: Text(
"Generate new address",
style: STextStyles.button.copyWith(
color: CFColors.stackAccent,
),
),
),
const SizedBox( const SizedBox(
height: 30, height: 30,
), ),
Center( RoundedWhiteContainer(
child: QrImage( child: Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: Column(
children: [
QrImage(
data: "${coin.uriScheme}:$receivingAddress", data: "${coin.uriScheme}:$receivingAddress",
size: MediaQuery.of(context).size.width / 2, size: MediaQuery.of(context).size.width / 2,
foregroundColor: CFColors.stackAccent, foregroundColor: CFColors.stackAccent,
), ),
),
const SizedBox( const SizedBox(
height: 30, height: 20,
), ),
// Spacer( BlueTextButton(
// flex: 7, text: "Create new QR code",
// ), onTap: () async {
TextButton( unawaited(Navigator.of(context).push(
onPressed: () {
Navigator.of(context).push(
RouteGenerator.getRoute( RouteGenerator.getRoute(
shouldUseMaterialRoute: shouldUseMaterialRoute:
RouteGenerator.useMaterialPageRoute, RouteGenerator.useMaterialPageRoute,
@ -149,17 +239,11 @@ class ReceiveView extends StatelessWidget {
name: GenerateUriQrCodeView.routeName, name: GenerateUriQrCodeView.routeName,
), ),
), ),
); ));
}, },
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(
CFColors.buttonGray,
), ),
],
), ),
child: Text(
"Generate QR Code",
style: STextStyles.button.copyWith(
color: CFColors.stackAccent,
), ),
), ),
), ),

View file

@ -532,19 +532,17 @@ class _WalletViewState extends ConsumerState<WalletView> {
onExchangePressed: () => onExchangePressed: () =>
_onExchangePressed(context), _onExchangePressed(context),
onReceivePressed: () async { onReceivePressed: () async {
final address = await ref
.read(managerProvider)
.currentReceivingAddress;
final coin = final coin =
ref.read(managerProvider).coin; ref.read(managerProvider).coin;
if (mounted) { if (mounted) {
unawaited(
Navigator.of(context).pushNamed( Navigator.of(context).pushNamed(
ReceiveView.routeName, ReceiveView.routeName,
arguments: Tuple2( arguments: Tuple2(
address, walletId,
coin, coin,
), ),
); ));
} }
}, },
onSendPressed: () { onSendPressed: () {

View file

@ -2,4 +2,4 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/models/exchange/change_now/currency.dart'; import 'package:stackwallet/models/exchange/change_now/currency.dart';
final availableChangeNowCurrenciesStateProvider = final availableChangeNowCurrenciesStateProvider =
StateProvider<List<Currency>>((ref) => []); StateProvider<List<Currency>>((ref) => <Currency>[]);

View file

@ -2,4 +2,5 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/models/exchange/change_now/available_floating_rate_pair.dart'; import 'package:stackwallet/models/exchange/change_now/available_floating_rate_pair.dart';
final availableFloatingRatePairsStateProvider = final availableFloatingRatePairsStateProvider =
StateProvider<List<AvailableFloatingRatePair>>((ref) => []); StateProvider<List<AvailableFloatingRatePair>>(
(ref) => <AvailableFloatingRatePair>[]);

View file

@ -681,7 +681,7 @@ class RouteGenerator {
return getRoute( return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute, shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => ReceiveView( builder: (_) => ReceiveView(
receivingAddress: args.item1, walletId: args.item1,
coin: args.item2, coin: args.item2,
), ),
settings: RouteSettings( settings: RouteSettings(

View file

@ -3825,4 +3825,34 @@ class BitcoinWallet extends CoinServiceAPI {
return available - estimatedFee; return available - estimatedFee;
} }
@override
Future<bool> generateNewAddress() async {
try {
await _incrementAddressIndexForChain(
0, DerivePathType.bip84); // First increment the receiving index
final newReceivingIndex = DB.instance.get<dynamic>(
boxName: walletId,
key: 'receivingIndexP2WPKH') as int; // Check the new receiving index
final newReceivingAddress = await _generateAddressForChain(
0,
newReceivingIndex,
DerivePathType
.bip84); // Use new index to derive a new receiving address
await _addToAddressesArrayForChain(
newReceivingAddress,
0,
DerivePathType
.bip84); // Add that new receiving address to the array of receiving addresses
_currentReceivingAddress = Future(() =>
newReceivingAddress); // Set the new receiving address that the service
return true;
} catch (e, s) {
Logging.instance.log(
"Exception rethrown from generateNewAddress(): $e\n$s",
level: LogLevel.Error);
return false;
}
}
} }

View file

@ -212,4 +212,6 @@ abstract class CoinServiceAPI {
bool get isConnected; bool get isConnected;
Future<int> estimateFeeFor(int satoshiAmount, int feeRate); Future<int> estimateFeeFor(int satoshiAmount, int feeRate);
Future<bool> generateNewAddress();
} }

View file

@ -3011,6 +3011,35 @@ class DogecoinWallet extends CoinServiceAPI {
return available - estimatedFee; return available - estimatedFee;
} }
Future<bool> generateNewAddress() async {
try {
await _incrementAddressIndexForChain(
0, DerivePathType.bip44); // First increment the receiving index
final newReceivingIndex = DB.instance.get<dynamic>(
boxName: walletId,
key: 'receivingIndexP2PKH') as int; // Check the new receiving index
final newReceivingAddress = await _generateAddressForChain(
0,
newReceivingIndex,
DerivePathType
.bip44); // Use new index to derive a new receiving address
await _addToAddressesArrayForChain(
newReceivingAddress,
0,
DerivePathType
.bip44); // Add that new receiving address to the array of receiving addresses
_currentReceivingAddressP2PKH = Future(() =>
newReceivingAddress); // Set the new receiving address that the service
return true;
} catch (e, s) {
Logging.instance.log(
"Exception rethrown from generateNewAddress(): $e\n$s",
level: LogLevel.Error);
return false;
}
}
} }
// Dogecoin Network // Dogecoin Network

View file

@ -77,11 +77,8 @@ Future<void> executeNative(Map<String, dynamic> arguments) async {
final startHeight = arguments['startHeight'] as int?; final startHeight = arguments['startHeight'] as int?;
final numberOfBlocks = arguments['numberOfBlocks'] as int?; final numberOfBlocks = arguments['numberOfBlocks'] as int?;
Map<String, dynamic> result = {}; Map<String, dynamic> result = {};
if (!(wallet == null || if (!(wallet == null || startHeight == null || numberOfBlocks == null)) {
startHeight == null || var outputs = await scanOutPuts(wallet, startHeight, numberOfBlocks);
numberOfBlocks == null)) {
var outputs =
await scanOutPuts(wallet, startHeight, numberOfBlocks);
result['outputs'] = outputs; result['outputs'] = outputs;
sendPort.send(result); sendPort.send(result);
return; return;
@ -111,8 +108,8 @@ Future<void> executeNative(Map<String, dynamic> arguments) async {
epicboxConfig == null)) { epicboxConfig == null)) {
Logging.instance Logging.instance
.log("SECRET_KEY_INDEX_IS $secretKeyIndex", level: LogLevel.Info); .log("SECRET_KEY_INDEX_IS $secretKeyIndex", level: LogLevel.Info);
result['result'] = await getSubscribeRequest( result['result'] =
wallet, secretKeyIndex, epicboxConfig); await getSubscribeRequest(wallet, secretKeyIndex, epicboxConfig);
sendPort.send(result); sendPort.send(result);
return; return;
} }
@ -122,8 +119,7 @@ Future<void> executeNative(Map<String, dynamic> arguments) async {
Map<String, dynamic> result = {}; Map<String, dynamic> result = {};
if (!(wallet == null || slates == null)) { if (!(wallet == null || slates == null)) {
result['result'] = result['result'] = await processSlates(wallet, slates.toString());
await processSlates(wallet, slates.toString());
sendPort.send(result); sendPort.send(result);
return; return;
} }
@ -135,8 +131,8 @@ Future<void> executeNative(Map<String, dynamic> arguments) async {
if (!(wallet == null || if (!(wallet == null ||
refreshFromNode == null || refreshFromNode == null ||
minimumConfirmations == null)) { minimumConfirmations == null)) {
var res = await getWalletInfo( var res =
wallet, refreshFromNode, minimumConfirmations); await getWalletInfo(wallet, refreshFromNode, minimumConfirmations);
result['result'] = res; result['result'] = res;
sendPort.send(result); sendPort.send(result);
return; return;
@ -166,11 +162,9 @@ Future<void> executeNative(Map<String, dynamic> arguments) async {
final amount = arguments['amount'] as int?; final amount = arguments['amount'] as int?;
final minimumConfirmations = arguments['minimumConfirmations'] as int?; final minimumConfirmations = arguments['minimumConfirmations'] as int?;
Map<String, dynamic> result = {}; Map<String, dynamic> result = {};
if (!(wallet == null || if (!(wallet == null || amount == null || minimumConfirmations == null)) {
amount == null || var res =
minimumConfirmations == null)) { await getTransactionFees(wallet, amount, minimumConfirmations);
var res = await getTransactionFees(
wallet, amount, minimumConfirmations);
result['result'] = res; result['result'] = res;
sendPort.send(result); sendPort.send(result);
return; return;
@ -198,7 +192,8 @@ Future<void> executeNative(Map<String, dynamic> arguments) async {
} }
} else if (function == "txHttpSend") { } else if (function == "txHttpSend") {
final wallet = arguments['wallet'] as String?; final wallet = arguments['wallet'] as String?;
final selectionStrategyIsAll = arguments['selectionStrategyIsAll'] as int?; final selectionStrategyIsAll =
arguments['selectionStrategyIsAll'] as int?;
final minimumConfirmations = arguments['minimumConfirmations'] as int?; final minimumConfirmations = arguments['minimumConfirmations'] as int?;
final message = arguments['message'] as String?; final message = arguments['message'] as String?;
final amount = arguments['amount'] as int?; final amount = arguments['amount'] as int?;
@ -218,7 +213,6 @@ Future<void> executeNative(Map<String, dynamic> arguments) async {
sendPort.send(result); sendPort.send(result);
return; return;
} }
} }
Logging.instance.log( Logging.instance.log(
"Error Arguments for $function not formatted correctly", "Error Arguments for $function not formatted correctly",
@ -245,8 +239,7 @@ void stop(ReceivePort port) {
// Keep Wrapper functions outside of the class to avoid memory leaks and errors about receive ports and illegal arguments. // Keep Wrapper functions outside of the class to avoid memory leaks and errors about receive ports and illegal arguments.
// TODO: Can get rid of this wrapper and call it in a full isolate instead of compute() if we want more control over this // TODO: Can get rid of this wrapper and call it in a full isolate instead of compute() if we want more control over this
Future<String> _cancelTransactionWrapper( Future<String> _cancelTransactionWrapper(Tuple2<String, String> data) async {
Tuple2<String, String> data) async {
// assuming this returns an empty string on success // assuming this returns an empty string on success
// or an error message string on failure // or an error message string on failure
return cancelTransaction(data.item1, data.item2); return cancelTransaction(data.item1, data.item2);
@ -290,8 +283,7 @@ Future<String> _initWalletWrapper(
Future<String> _initGetAddressInfoWrapper( Future<String> _initGetAddressInfoWrapper(
Tuple3<String, int, String> data) async { Tuple3<String, int, String> data) async {
String walletAddress = String walletAddress = getAddressInfo(data.item1, data.item2, data.item3);
getAddressInfo(data.item1, data.item2, data.item3);
return walletAddress; return walletAddress;
} }
@ -754,7 +746,8 @@ class EpicCashWallet extends CoinServiceAPI {
String receiverAddress = txData['addresss'] as String; String receiverAddress = txData['addresss'] as String;
await m.protect(() async { await m.protect(() async {
if (receiverAddress.startsWith("http://") || receiverAddress.startsWith("https://")) { if (receiverAddress.startsWith("http://") ||
receiverAddress.startsWith("https://")) {
const int selectionStrategyIsAll = 0; const int selectionStrategyIsAll = 0;
ReceivePort receivePort = await getIsolate({ ReceivePort receivePort = await getIsolate({
"function": "txHttpSend", "function": "txHttpSend",
@ -774,9 +767,8 @@ class EpicCashWallet extends CoinServiceAPI {
throw Exception("txHttpSend isolate failed"); throw Exception("txHttpSend isolate failed");
} }
stop(receivePort); stop(receivePort);
Logging.instance.log('Closing txHttpSend!\n $message', Logging.instance
level: LogLevel.Info); .log('Closing txHttpSend!\n $message', level: LogLevel.Info);
} else { } else {
ReceivePort receivePort = await getIsolate({ ReceivePort receivePort = await getIsolate({
"function": "createTransaction", "function": "createTransaction",
@ -809,7 +801,6 @@ class EpicCashWallet extends CoinServiceAPI {
await putSendToAddresses(sendTx); await putSendToAddresses(sendTx);
Logging.instance.log("CONFIRM_RESULT_IS $sendTx", level: LogLevel.Info); Logging.instance.log("CONFIRM_RESULT_IS $sendTx", level: LogLevel.Info);
final decodeData = json.decode(sendTx); final decodeData = json.decode(sendTx);
@ -818,9 +809,9 @@ class EpicCashWallet extends CoinServiceAPI {
String errorMessage = decodeData[1] as String; String errorMessage = decodeData[1] as String;
throw Exception("Transaction failed with error code $errorMessage"); throw Exception("Transaction failed with error code $errorMessage");
} else { } else {
//If it's HTTP send no need to post to epicbox //If it's HTTP send no need to post to epicbox
if (!(receiverAddress.startsWith("http://") || receiverAddress.startsWith("https://"))) { if (!(receiverAddress.startsWith("http://") ||
receiverAddress.startsWith("https://"))) {
final postSlateRequest = decodeData[1]; final postSlateRequest = decodeData[1];
final postToServer = await postSlate( final postToServer = await postSlate(
txData['addresss'] as String, postSlateRequest as String); txData['addresss'] as String, postSlateRequest as String);
@ -969,10 +960,9 @@ class EpicCashWallet extends CoinServiceAPI {
level: LogLevel.Info); level: LogLevel.Info);
final config = await getRealConfig(); final config = await getRealConfig();
final password = final password = await _secureStore.read(key: '${_walletId}_password');
await _secureStore.read(key: '${_walletId}_password');
final walletOpen = openWallet(config!, password!); final walletOpen = openWallet(config, password!);
await _secureStore.write(key: '${_walletId}_wallet', value: walletOpen); await _secureStore.write(key: '${_walletId}_wallet', value: walletOpen);
if ((DB.instance.get<dynamic>(boxName: walletId, key: "id")) == null) { if ((DB.instance.get<dynamic>(boxName: walletId, key: "id")) == null) {
@ -1297,7 +1287,6 @@ class EpicCashWallet extends CoinServiceAPI {
Future<bool> startScans() async { Future<bool> startScans() async {
try { try {
final wallet = await _secureStore.read(key: '${_walletId}_wallet'); final wallet = await _secureStore.read(key: '${_walletId}_wallet');
var restoreHeight = var restoreHeight =
@ -1445,7 +1434,6 @@ class EpicCashWallet extends CoinServiceAPI {
//Store Epic box address info //Store Epic box address info
await storeEpicboxInfo(); await storeEpicboxInfo();
} catch (e, s) { } catch (e, s) {
Logging.instance Logging.instance
.log("Error recovering wallet $e\n$s", level: LogLevel.Error); .log("Error recovering wallet $e\n$s", level: LogLevel.Error);
@ -1724,11 +1712,13 @@ class EpicCashWallet extends CoinServiceAPI {
subscribeRequest['signature'] as String, slate as String); subscribeRequest['signature'] as String, slate as String);
} }
if (response.contains("Error Wallet store error: DB Not Found Error")) { if (response
.contains("Error Wallet store error: DB Not Found Error")) {
//Already processed - to be deleted //Already processed - to be deleted
Logging.instance.log("DELETING_PROCESSED_SLATE", Logging.instance
level: LogLevel.Info); .log("DELETING_PROCESSED_SLATE", level: LogLevel.Info);
final slateDelete = await deleteSlate(currentAddress, subscribeRequest['signature'] as String, slate as String); final slateDelete = await deleteSlate(currentAddress,
subscribeRequest['signature'] as String, slate as String);
Logging.instance.log("DELETE_SLATE_RESPONSE $slateDelete", Logging.instance.log("DELETE_SLATE_RESPONSE $slateDelete",
level: LogLevel.Info); level: LogLevel.Info);
} else { } else {
@ -1738,7 +1728,7 @@ class EpicCashWallet extends CoinServiceAPI {
if (slateStatus == "PendingProcessing") { if (slateStatus == "PendingProcessing") {
//Encrypt slate //Encrypt slate
String encryptedSlate = await getEncryptedSlate( String encryptedSlate = await getEncryptedSlate(
wallet!, wallet,
slateSender, slateSender,
currentReceivingIndex, currentReceivingIndex,
epicboxConfig!, epicboxConfig!,
@ -1753,7 +1743,8 @@ class EpicCashWallet extends CoinServiceAPI {
level: LogLevel.Info); level: LogLevel.Info);
} else { } else {
//Finalise Slate //Finalise Slate
final processSlate = json.decode(decodedResponse[1] as String); final processSlate =
json.decode(decodedResponse[1] as String);
Logging.instance.log( Logging.instance.log(
"PROCESSED_SLATE_TO_FINALIZE $processSlate", "PROCESSED_SLATE_TO_FINALIZE $processSlate",
level: LogLevel.Info); level: LogLevel.Info);
@ -1762,8 +1753,7 @@ class EpicCashWallet extends CoinServiceAPI {
String txSlateId = tx[0]['tx_slate_id'] as String; String txSlateId = tx[0]['tx_slate_id'] as String;
Logging.instance Logging.instance
.log("TX_SLATE_ID_IS $txSlateId", level: LogLevel.Info); .log("TX_SLATE_ID_IS $txSlateId", level: LogLevel.Info);
final postToNode = await postSlateToNode( final postToNode = await postSlateToNode(wallet, txSlateId);
wallet!, txSlateId);
await deleteSlate(currentAddress, await deleteSlate(currentAddress,
subscribeRequest['signature'] as String, slate as String); subscribeRequest['signature'] as String, slate as String);
Logging.instance.log("POST_SLATE_RESPONSE $postToNode", Logging.instance.log("POST_SLATE_RESPONSE $postToNode",
@ -2316,4 +2306,29 @@ class EpicCashWallet extends CoinServiceAPI {
// TODO: implement this // TODO: implement this
return currentFee; return currentFee;
} }
// not used in epic currently
@override
Future<bool> generateNewAddress() async {
try {
// await incrementAddressIndexForChain(
// 0); // First increment the receiving index
// final newReceivingIndex =
// DB.instance.get<dynamic>(boxName: walletId, key: 'receivingIndex')
// as int; // Check the new receiving index
// final newReceivingAddress = await _generateAddressForChain(0,
// newReceivingIndex); // Use new index to derive a new receiving address
// await addToAddressesArrayForChain(newReceivingAddress,
// 0); // Add that new receiving address to the array of receiving addresses
// _currentReceivingAddress = Future(() =>
// newReceivingAddress); // Set the new receiving address that the service
return true;
} catch (e, s) {
Logging.instance.log(
"Exception rethrown from generateNewAddress(): $e\n$s",
level: LogLevel.Error);
return false;
}
}
} }

View file

@ -3858,4 +3858,28 @@ class FiroWallet extends CoinServiceAPI {
rethrow; rethrow;
} }
} }
@override
Future<bool> generateNewAddress() async {
try {
await incrementAddressIndexForChain(
0); // First increment the receiving index
final newReceivingIndex =
DB.instance.get<dynamic>(boxName: walletId, key: 'receivingIndex')
as int; // Check the new receiving index
final newReceivingAddress = await _generateAddressForChain(0,
newReceivingIndex); // Use new index to derive a new receiving address
await addToAddressesArrayForChain(newReceivingAddress,
0); // Add that new receiving address to the array of receiving addresses
_currentReceivingAddress = Future(() =>
newReceivingAddress); // Set the new receiving address that the service
return true;
} catch (e, s) {
Logging.instance.log(
"Exception rethrown from generateNewAddress(): $e\n$s",
level: LogLevel.Error);
return false;
}
}
} }

View file

@ -266,4 +266,12 @@ class Manager with ChangeNotifier {
Future<int> estimateFeeFor(int satoshiAmount, int feeRate) async { Future<int> estimateFeeFor(int satoshiAmount, int feeRate) async {
return _currentWallet.estimateFeeFor(satoshiAmount, feeRate); return _currentWallet.estimateFeeFor(satoshiAmount, feeRate);
} }
Future<bool> generateNewAddress() async {
final success = await _currentWallet.generateNewAddress();
if (success) {
notifyListeners();
}
return success;
}
} }

View file

@ -1521,4 +1521,33 @@ class MoneroWallet extends CoinServiceAPI {
10000; 10000;
return fee; return fee;
} }
@override
Future<bool> generateNewAddress() async {
try {
const String indexKey = "receivingIndex";
// First increment the receiving index
await _incrementAddressIndexForChain(0);
final newReceivingIndex =
DB.instance.get<dynamic>(boxName: walletId, key: indexKey) as int;
// Use new index to derive a new receiving address
final newReceivingAddress =
await _generateAddressForChain(0, newReceivingIndex);
// Add that new receiving address to the array of receiving addresses
await _addToAddressesArrayForChain(newReceivingAddress, 0);
// Set the new receiving address that the service
_currentReceivingAddress = Future(() => newReceivingAddress);
return true;
} catch (e, s) {
Logging.instance.log(
"Exception rethrown from generateNewAddress(): $e\n$s",
level: LogLevel.Error);
return false;
}
}
} }

View file

@ -84,6 +84,7 @@ class _SVG {
String get solidSliders => "assets/svg/sliders-solid.svg"; String get solidSliders => "assets/svg/sliders-solid.svg";
String get questionMessage => "assets/svg/message-question.svg"; String get questionMessage => "assets/svg/message-question.svg";
String get envelope => "assets/svg/envelope.svg"; String get envelope => "assets/svg/envelope.svg";
String get share => "assets/svg/share-2.svg";
String get receive => "assets/svg/tx-icon-receive.svg"; String get receive => "assets/svg/tx-icon-receive.svg";
String get receivePending => "assets/svg/tx-icon-receive-pending.svg"; String get receivePending => "assets/svg/tx-icon-receive-pending.svg";

View file

@ -33,7 +33,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 currentDbVersion = 0; static const int currentHiveDbVersion = 1;
static List<int> possibleLengthsForCoin(Coin coin) { static List<int> possibleLengthsForCoin(Coin coin) {
final List<int> values = []; final List<int> values = [];

View file

@ -1,5 +1,16 @@
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:hive/hive.dart';
import 'package:stackwallet/electrumx_rpc/electrumx.dart';
import 'package:stackwallet/hive/db.dart';
import 'package:stackwallet/models/lelantus_coin.dart';
import 'package:stackwallet/models/node_model.dart';
import 'package:stackwallet/services/node_service.dart';
import 'package:stackwallet/services/wallets_service.dart';
import 'package:stackwallet/utilities/default_nodes.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'; import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/prefs.dart';
class DbVersionMigrator { class DbVersionMigrator {
Future<void> migrate( Future<void> migrate(
@ -8,83 +19,107 @@ class DbVersionMigrator {
FlutterSecureStorage(), FlutterSecureStorage(),
), ),
}) async { }) async {
// final wallets = await Hive.openBox('wallets'); switch (fromVersion) {
// final names = Map<String, String>.from((await wallets.get("names")) ?? {}); case 0:
// await Hive.openBox<dynamic>(DB.boxNameAllWalletsData);
// switch (fromVersion) { await Hive.openBox<dynamic>(DB.boxNamePrefs);
// case 0: final walletsService = WalletsService();
// // migrate each final nodeService = NodeService();
// for (final entry in names.entries) { final prefs = Prefs.instance;
// final walletId = entry.value; final walletInfoList = await walletsService.walletNames;
// final walletName = entry.key; await prefs.init();
//
// // move main/test network to walletId based ElectrumX? client;
// final network = await wallets.get("${entry.key}_network"); int? latestSetId;
// await wallets.put("${walletId}_network", network);
// await wallets.delete("${walletName}_network"); // only instantiate client if there are firo wallets
// if (walletInfoList.values.any((element) => element.coin == Coin.firo)) {
// final old = await Hive.openBox(walletName); await Hive.openBox<NodeModel>(DB.boxNameNodeModels);
// final wallet = await Hive.openBox(walletId); await Hive.openBox<NodeModel>(DB.boxNamePrimaryNodes);
// final node = nodeService.getPrimaryNodeFor(coin: Coin.firo) ??
// // notes DefaultNodes.firo;
// final oldNotes = await old.get("notes"); List<ElectrumXNode> failovers = nodeService
// await wallet.put("notes", oldNotes); .failoverNodesFor(coin: Coin.firo)
// await old.delete("notes"); .map(
// (e) => ElectrumXNode(
// // address book address: e.host,
// final addressBook = await old.get("addressBookEntries"); port: e.port,
// await wallet.put("addressBookEntries", addressBook); name: e.name,
// await old.put("addressBookEntries", null); id: e.id,
// useSSL: e.useSSL,
// // receiveDerivations ),
// Map<String, dynamic> newReceiveDerivations = {}; )
// final receiveDerivations = .toList();
// Map<int, dynamic>.from(await old.get("receiveDerivations") ?? {});
// client = ElectrumX.from(
// for (int i = 0; i < receiveDerivations.length; i++) { node: ElectrumXNode(
// receiveDerivations[i].remove("fingerprint"); address: node.host,
// receiveDerivations[i].remove("identifier"); port: node.port,
// receiveDerivations[i].remove("privateKey"); name: node.name,
// newReceiveDerivations["$i"] = receiveDerivations[i]; id: node.id,
// } useSSL: node.useSSL),
// final receiveDerivationsString = jsonEncode(newReceiveDerivations); prefs: prefs,
// failovers: failovers,
// await secureStore.write( );
// key: "${walletId}_receiveDerivations",
// value: receiveDerivationsString); try {
// await old.delete("receiveDerivations"); latestSetId = await client.getLatestCoinId();
// } catch (e) {
// // changeDerivations // default to 2 for now
// Map<String, dynamic> newChangeDerivations = {}; latestSetId = 2;
// final changeDerivations = Logging.instance.log(
// Map<int, dynamic>.from(await old.get("changeDerivations") ?? {}); "Failed to fetch latest coin id during firo db migrate: $e \nUsing a default value of 2",
// level: LogLevel.Warning);
// for (int i = 0; i < changeDerivations.length; i++) { }
// changeDerivations[i].remove("fingerprint"); }
// changeDerivations[i].remove("identifier");
// changeDerivations[i].remove("privateKey"); for (final walletInfo in walletInfoList.values) {
// newChangeDerivations["$i"] = changeDerivations[i]; // migrate each firo wallet's lelantus coins
// } if (walletInfo.coin == Coin.firo) {
// final changeDerivationsString = jsonEncode(newChangeDerivations); await Hive.openBox<dynamic>(walletInfo.walletId);
// final _lelantusCoins = DB.instance.get<dynamic>(
// await secureStore.write( boxName: walletInfo.walletId, key: '_lelantus_coins') as List?;
// key: "${walletId}_changeDerivations", final List<Map<dynamic, LelantusCoin>> lelantusCoins = [];
// value: changeDerivationsString); for (var lCoin in _lelantusCoins ?? []) {
// await old.delete("changeDerivations"); lelantusCoins
// } .add({lCoin.keys.first: lCoin.values.first as LelantusCoin});
// }
// // finally update version
// await wallets.put("db_version", 1); List<Map<dynamic, LelantusCoin>> coins = [];
// for (final element in lelantusCoins) {
// return; LelantusCoin coin = element.values.first;
// // not needed yet int anonSetId = coin.anonymitySetId;
// // return migrate(1); if (coin.anonymitySetId == 1 &&
// (coin.publicCoin == '' ||
// // case 1: coin.publicCoin == "jmintData.publicCoin")) {
// // return migrate(2); anonSetId = latestSetId!;
// }
// default: coins.add({
// return; element.keys.first: LelantusCoin(coin.index, coin.value,
// } coin.publicCoin, coin.txId, anonSetId, coin.isUsed)
});
}
Logger.print("newcoins $coins", normalLength: false);
await DB.instance.put<dynamic>(
boxName: walletInfo.walletId,
key: '_lelantus_coins',
value: coins);
}
}
// finally update version
await DB.instance.put<dynamic>(
boxName: DB.boxNameDBInfo, key: "hive_data_version", value: 1);
return;
// not needed yet
// return migrate(1);
// case 1:
// return migrate(2);
default:
return;
}
} }
} }

View file

@ -255,6 +255,7 @@ flutter:
- assets/svg/sliders-solid.svg - assets/svg/sliders-solid.svg
- assets/svg/message-question.svg - assets/svg/message-question.svg
- assets/svg/envelope.svg - assets/svg/envelope.svg
- assets/svg/share-2.svg
# coin icons # coin icons
- assets/svg/coin_icons/Bitcoin.svg - assets/svg/coin_icons/Bitcoin.svg
- assets/svg/coin_icons/Dogecoin.svg - assets/svg/coin_icons/Dogecoin.svg

69
test/hive/db_test.dart Normal file
View file

@ -0,0 +1,69 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:hive_test/hive_test.dart';
import 'package:stackwallet/hive/db.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
void main() {
group("DB box names", () {
test("address book", () => expect(DB.boxNameAddressBook, "addressBook"));
test("debug info", () => expect(DB.boxNameDebugInfo, "debugInfoBox"));
test("nodes", () => expect(DB.boxNameNodeModels, "nodeModels"));
test("primary nodes", () => expect(DB.boxNamePrimaryNodes, "primaryNodes"));
test("wallets info", () => expect(DB.boxNameAllWalletsData, "wallets"));
test("notifications",
() => expect(DB.boxNameNotifications, "notificationModels"));
test(
"watched transactions",
() => expect(
DB.boxNameWatchedTransactions, "watchedTxNotificationModels"));
test(
"watched trades",
() =>
expect(DB.boxNameWatchedTrades, "watchedTradesNotificationModels"));
test("trades", () => expect(DB.boxNameTrades, "exchangeTransactionsBox"));
test("trade notes", () => expect(DB.boxNameTradeNotes, "tradeNotesBox"));
test("tx <> trade lookup table",
() => expect(DB.boxNameTradeLookup, "tradeToTxidLookUpBox"));
test("favorite wallets",
() => expect(DB.boxNameFavoriteWallets, "favoriteWallets"));
test("preferences", () => expect(DB.boxNamePrefs, "prefs"));
test(
"deleted wallets to clear out on start",
() =>
expect(DB.boxNameWalletsToDeleteOnStart, "walletsToDeleteOnStart"));
test("price cache",
() => expect(DB.boxNamePriceCache, "priceAPIPrice24hCache"));
test("boxNameTxCache", () {
for (final coin in Coin.values) {
expect(DB.instance.boxNameTxCache(coin: coin), "${coin.name}_txCache");
}
});
test("boxNameSetCache", () {
for (final coin in Coin.values) {
expect(DB.instance.boxNameSetCache(coin: coin),
"${coin.name}_anonymitySetCache");
}
});
test("boxNameUsedSerialsCache", () {
for (final coin in Coin.values) {
expect(DB.instance.boxNameUsedSerialsCache(coin: coin),
"${coin.name}_usedSerialsCache");
}
});
});
group("tests requiring test hive environment", () {
setUp(() async {
await setUpTestHive();
});
test("DB init", () async {});
tearDown(() async {
await tearDownTestHive();
});
});
}

View file

@ -0,0 +1,215 @@
import 'package:decimal/decimal.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:stackwallet/models/exchange/change_now/change_now_response.dart';
import 'package:stackwallet/models/exchange/change_now/currency.dart';
import 'package:stackwallet/models/exchange/change_now/estimated_exchange_amount.dart';
import 'package:stackwallet/models/exchange/estimated_rate_exchange_form_state.dart';
import 'package:stackwallet/services/change_now/change_now.dart';
import 'estimated_rate_exchange_form_state_test.mocks.dart';
@GenerateMocks([ChangeNow])
void main() {
final currencyA = Currency(
ticker: "btc",
name: "Bitcoin",
image: "image.url",
hasExternalId: false,
isFiat: false,
featured: false,
isStable: true,
supportsFixedRate: true,
);
final currencyB = Currency(
ticker: "xmr",
name: "Monero",
image: "image.url",
hasExternalId: false,
isFiat: false,
featured: false,
isStable: true,
supportsFixedRate: true,
);
final currencyC = Currency(
ticker: "firo",
name: "Firo",
image: "image.url",
hasExternalId: false,
isFiat: false,
featured: false,
isStable: true,
supportsFixedRate: true,
);
test("EstimatedRateExchangeFormState constructor", () async {
final state = EstimatedRateExchangeFormState();
expect(state.from, null);
expect(state.to, null);
expect(state.canExchange, false);
expect(state.rate, null);
expect(state.rateDisplayString, "N/A");
expect(state.fromAmountString, "");
expect(state.toAmountString, "");
expect(state.minimumSendWarning, "");
});
test("init EstimatedRateExchangeFormState", () async {
final state = EstimatedRateExchangeFormState();
await state.init(currencyA, currencyB);
expect(state.from, currencyA);
expect(state.to, currencyB);
expect(state.canExchange, false);
expect(state.rate, null);
expect(state.rateDisplayString, "N/A");
expect(state.fromAmountString, "");
expect(state.toAmountString, "");
expect(state.minimumSendWarning, "");
});
test("updateTo on fresh state", () async {
final state = EstimatedRateExchangeFormState();
await state.updateTo(currencyA, false);
expect(state.from, null);
expect(state.to, currencyA);
expect(state.canExchange, false);
expect(state.rate, null);
expect(state.rateDisplayString, "N/A");
expect(state.fromAmountString, "");
expect(state.toAmountString, "");
expect(state.minimumSendWarning, "");
});
test(
"updateTo after updateFrom where amounts are null and getMinimalExchangeAmount succeeds",
() async {
final cn = MockChangeNow();
final state = EstimatedRateExchangeFormState();
state.cnTesting = cn;
when(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr"))
.thenAnswer((_) async => ChangeNowResponse(value: Decimal.fromInt(42)));
await state.updateFrom(currencyA, true);
await state.updateTo(currencyB, true);
expect(state.from, currencyA);
expect(state.to, currencyB);
expect(state.canExchange, false);
expect(state.rate, null);
expect(state.rateDisplayString, "N/A");
expect(state.fromAmountString, "");
expect(state.toAmountString, "");
expect(state.minimumSendWarning, "");
verify(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr"))
.called(1);
});
test(
"updateTo after updateFrom where amounts are null and getMinimalExchangeAmount fails",
() async {
final cn = MockChangeNow();
final state = EstimatedRateExchangeFormState();
state.cnTesting = cn;
when(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr"))
.thenAnswer((_) async => ChangeNowResponse());
await state.updateFrom(currencyA, true);
await state.updateTo(currencyB, true);
expect(state.from, currencyA);
expect(state.to, currencyB);
expect(state.canExchange, false);
expect(state.rate, null);
expect(state.rateDisplayString, "N/A");
expect(state.fromAmountString, "");
expect(state.toAmountString, "");
expect(state.minimumSendWarning, "");
verify(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr"))
.called(1);
});
test(
"updateTo after updateFrom and setFromAmountAndCalculateToAmount where fromAmount is less than the minimum required exchange amount",
() async {
final cn = MockChangeNow();
final state = EstimatedRateExchangeFormState();
state.cnTesting = cn;
when(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr"))
.thenAnswer((_) async => ChangeNowResponse(value: Decimal.fromInt(42)));
await state.updateFrom(currencyA, true);
await state.setFromAmountAndCalculateToAmount(Decimal.parse("10.10"), true);
await state.updateTo(currencyB, true);
expect(state.from, currencyA);
expect(state.to, currencyB);
expect(state.canExchange, false);
expect(state.rate, null);
expect(state.rateDisplayString, "N/A");
expect(state.fromAmountString, "10.10000000");
expect(state.toAmountString, "");
expect(state.minimumSendWarning, "Minimum amount 42 BTC");
verify(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr"))
.called(1);
});
test(
"updateTo after updateFrom and setFromAmountAndCalculateToAmount where fromAmount is greater than the minimum required exchange amount",
() async {
final cn = MockChangeNow();
final state = EstimatedRateExchangeFormState();
state.cnTesting = cn;
when(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr"))
.thenAnswer((_) async => ChangeNowResponse(value: Decimal.fromInt(42)));
when(cn.getEstimatedExchangeAmount(
fromTicker: "btc",
toTicker: "xmr",
fromAmount: Decimal.parse("110.10")))
.thenAnswer((_) async => ChangeNowResponse(
value: EstimatedExchangeAmount(
transactionSpeedForecast: '10-60',
rateId: 'some rate id',
warningMessage: '',
estimatedAmount: Decimal.parse("302.002348"),
)));
await state.updateFrom(currencyA, true);
await state.setFromAmountAndCalculateToAmount(
Decimal.parse("110.10"), true);
await state.updateTo(currencyB, true);
expect(state.from, currencyA);
expect(state.to, currencyB);
expect(state.canExchange, true);
expect(state.rate, Decimal.parse("2.742982270663"));
expect(state.rateDisplayString, "1 BTC ~2.74298227 XMR");
expect(state.fromAmountString, "110.10000000");
expect(state.toAmountString, "302.00234800");
expect(state.minimumSendWarning, "");
verify(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr"))
.called(1);
verify(cn.getEstimatedExchangeAmount(
fromTicker: "btc",
toTicker: "xmr",
fromAmount: Decimal.parse("110.10")))
.called(1);
});
}

View file

@ -0,0 +1,212 @@
// Mocks generated by Mockito 5.2.0 from annotations
// in stackwallet/test/models/exchange/estimated_rate_exchange_form_state_test.dart.
// Do not manually edit this file.
import 'dart:async' as _i5;
import 'package:decimal/decimal.dart' as _i7;
import 'package:http/http.dart' as _i4;
import 'package:mockito/mockito.dart' as _i1;
import 'package:stackwallet/models/exchange/change_now/available_floating_rate_pair.dart'
as _i12;
import 'package:stackwallet/models/exchange/change_now/change_now_response.dart'
as _i2;
import 'package:stackwallet/models/exchange/change_now/currency.dart' as _i6;
import 'package:stackwallet/models/exchange/change_now/estimated_exchange_amount.dart'
as _i8;
import 'package:stackwallet/models/exchange/change_now/exchange_transaction.dart'
as _i10;
import 'package:stackwallet/models/exchange/change_now/exchange_transaction_status.dart'
as _i11;
import 'package:stackwallet/models/exchange/change_now/fixed_rate_market.dart'
as _i9;
import 'package:stackwallet/services/change_now/change_now.dart' as _i3;
// ignore_for_file: type=lint
// ignore_for_file: avoid_redundant_argument_values
// ignore_for_file: avoid_setters_without_getters
// ignore_for_file: comment_references
// ignore_for_file: implementation_imports
// ignore_for_file: invalid_use_of_visible_for_testing_member
// ignore_for_file: prefer_const_constructors
// ignore_for_file: unnecessary_parenthesis
// ignore_for_file: camel_case_types
class _FakeChangeNowResponse_0<T> extends _i1.Fake
implements _i2.ChangeNowResponse<T> {}
/// A class which mocks [ChangeNow].
///
/// See the documentation for Mockito's code generation for more information.
class MockChangeNow extends _i1.Mock implements _i3.ChangeNow {
MockChangeNow() {
_i1.throwOnMissingStub(this);
}
@override
set client(_i4.Client? _client) =>
super.noSuchMethod(Invocation.setter(#client, _client),
returnValueForMissingStub: null);
@override
_i5.Future<_i2.ChangeNowResponse<List<_i6.Currency>>> getAvailableCurrencies(
{bool? fixedRate, bool? active}) =>
(super.noSuchMethod(
Invocation.method(#getAvailableCurrencies, [],
{#fixedRate: fixedRate, #active: active}),
returnValue: Future<_i2.ChangeNowResponse<List<_i6.Currency>>>.value(
_FakeChangeNowResponse_0<List<_i6.Currency>>())) as _i5
.Future<_i2.ChangeNowResponse<List<_i6.Currency>>>);
@override
_i5.Future<_i2.ChangeNowResponse<List<_i6.Currency>>> getPairedCurrencies(
{String? ticker, bool? fixedRate}) =>
(super.noSuchMethod(
Invocation.method(#getPairedCurrencies, [],
{#ticker: ticker, #fixedRate: fixedRate}),
returnValue: Future<_i2.ChangeNowResponse<List<_i6.Currency>>>.value(
_FakeChangeNowResponse_0<List<_i6.Currency>>())) as _i5
.Future<_i2.ChangeNowResponse<List<_i6.Currency>>>);
@override
_i5.Future<_i2.ChangeNowResponse<_i7.Decimal>> getMinimalExchangeAmount(
{String? fromTicker, String? toTicker, String? apiKey}) =>
(super.noSuchMethod(
Invocation.method(#getMinimalExchangeAmount, [], {
#fromTicker: fromTicker,
#toTicker: toTicker,
#apiKey: apiKey
}),
returnValue: Future<_i2.ChangeNowResponse<_i7.Decimal>>.value(
_FakeChangeNowResponse_0<_i7.Decimal>()))
as _i5.Future<_i2.ChangeNowResponse<_i7.Decimal>>);
@override
_i5.Future<_i2.ChangeNowResponse<_i8.EstimatedExchangeAmount>>
getEstimatedExchangeAmount(
{String? fromTicker,
String? toTicker,
_i7.Decimal? fromAmount,
String? apiKey}) =>
(super.noSuchMethod(
Invocation.method(#getEstimatedExchangeAmount, [], {
#fromTicker: fromTicker,
#toTicker: toTicker,
#fromAmount: fromAmount,
#apiKey: apiKey
}),
returnValue: Future<
_i2.ChangeNowResponse<
_i8.EstimatedExchangeAmount>>.value(
_FakeChangeNowResponse_0<_i8.EstimatedExchangeAmount>()))
as _i5
.Future<_i2.ChangeNowResponse<_i8.EstimatedExchangeAmount>>);
@override
_i5.Future<_i2.ChangeNowResponse<_i8.EstimatedExchangeAmount>>
getEstimatedFixedRateExchangeAmount(
{String? fromTicker,
String? toTicker,
_i7.Decimal? fromAmount,
bool? useRateId = true,
String? apiKey}) =>
(super.noSuchMethod(
Invocation.method(#getEstimatedFixedRateExchangeAmount, [], {
#fromTicker: fromTicker,
#toTicker: toTicker,
#fromAmount: fromAmount,
#useRateId: useRateId,
#apiKey: apiKey
}),
returnValue: Future<
_i2.ChangeNowResponse<
_i8.EstimatedExchangeAmount>>.value(
_FakeChangeNowResponse_0<_i8.EstimatedExchangeAmount>()))
as _i5
.Future<_i2.ChangeNowResponse<_i8.EstimatedExchangeAmount>>);
@override
_i5.Future<_i2.ChangeNowResponse<List<_i9.FixedRateMarket>>>
getAvailableFixedRateMarkets({String? apiKey}) => (super.noSuchMethod(
Invocation.method(
#getAvailableFixedRateMarkets, [], {#apiKey: apiKey}),
returnValue:
Future<_i2.ChangeNowResponse<List<_i9.FixedRateMarket>>>.value(
_FakeChangeNowResponse_0<List<_i9.FixedRateMarket>>())) as _i5
.Future<_i2.ChangeNowResponse<List<_i9.FixedRateMarket>>>);
@override
_i5.Future<_i2.ChangeNowResponse<_i10.ExchangeTransaction>>
createStandardExchangeTransaction(
{String? fromTicker,
String? toTicker,
String? receivingAddress,
_i7.Decimal? amount,
String? extraId = r'',
String? userId = r'',
String? contactEmail = r'',
String? refundAddress = r'',
String? refundExtraId = r'',
String? apiKey}) =>
(super.noSuchMethod(
Invocation.method(#createStandardExchangeTransaction, [], {
#fromTicker: fromTicker,
#toTicker: toTicker,
#receivingAddress: receivingAddress,
#amount: amount,
#extraId: extraId,
#userId: userId,
#contactEmail: contactEmail,
#refundAddress: refundAddress,
#refundExtraId: refundExtraId,
#apiKey: apiKey
}),
returnValue: Future<
_i2.ChangeNowResponse<_i10.ExchangeTransaction>>.value(
_FakeChangeNowResponse_0<_i10.ExchangeTransaction>())) as _i5
.Future<_i2.ChangeNowResponse<_i10.ExchangeTransaction>>);
@override
_i5.Future<_i2.ChangeNowResponse<_i10.ExchangeTransaction>>
createFixedRateExchangeTransaction(
{String? fromTicker,
String? toTicker,
String? receivingAddress,
_i7.Decimal? amount,
String? rateId,
String? extraId = r'',
String? userId = r'',
String? contactEmail = r'',
String? refundAddress = r'',
String? refundExtraId = r'',
String? apiKey}) =>
(super.noSuchMethod(
Invocation.method(#createFixedRateExchangeTransaction, [], {
#fromTicker: fromTicker,
#toTicker: toTicker,
#receivingAddress: receivingAddress,
#amount: amount,
#rateId: rateId,
#extraId: extraId,
#userId: userId,
#contactEmail: contactEmail,
#refundAddress: refundAddress,
#refundExtraId: refundExtraId,
#apiKey: apiKey
}),
returnValue: Future<
_i2.ChangeNowResponse<_i10.ExchangeTransaction>>.value(
_FakeChangeNowResponse_0<_i10.ExchangeTransaction>())) as _i5
.Future<_i2.ChangeNowResponse<_i10.ExchangeTransaction>>);
@override
_i5.Future<_i2.ChangeNowResponse<_i11.ExchangeTransactionStatus>>
getTransactionStatus({String? id, String? apiKey}) => (super.noSuchMethod(
Invocation.method(
#getTransactionStatus, [], {#id: id, #apiKey: apiKey}),
returnValue:
Future<_i2.ChangeNowResponse<_i11.ExchangeTransactionStatus>>.value(
_FakeChangeNowResponse_0<_i11.ExchangeTransactionStatus>())) as _i5
.Future<_i2.ChangeNowResponse<_i11.ExchangeTransactionStatus>>);
@override
_i5.Future<_i2.ChangeNowResponse<List<_i12.AvailableFloatingRatePair>>>
getAvailableFloatingRatePairs({bool? includePartners = false}) => (super
.noSuchMethod(
Invocation.method(#getAvailableFloatingRatePairs, [],
{#includePartners: includePartners}),
returnValue:
Future<_i2.ChangeNowResponse<List<_i12.AvailableFloatingRatePair>>>.value(
_FakeChangeNowResponse_0<List<_i12.AvailableFloatingRatePair>>())) as _i5
.Future<_i2.ChangeNowResponse<List<_i12.AvailableFloatingRatePair>>>);
}

View file

@ -0,0 +1,15 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:stackwallet/models/isar/models/log.dart';
import 'package:stackwallet/utilities/logger.dart';
void main() {
test("Log class", () {
final log = Log()
..message = "hello"
..timestampInMillisUTC = 100000001
..logLevel = LogLevel.Fatal;
expect(log.toString(), "[Fatal][1970-01-02 03:46:40.001Z]: hello");
expect(log.id, -9223372036854775808);
});
}

View file

@ -2,20 +2,33 @@
// in stackwallet/test/screen_tests/exchange/exchange_view_test.dart. // in stackwallet/test/screen_tests/exchange/exchange_view_test.dart.
// Do not manually edit this file. // Do not manually edit this file.
import 'dart:async' as _i6; import 'dart:async' as _i7;
import 'dart:ui' as _i7; import 'dart:ui' as _i8;
import 'package:decimal/decimal.dart' as _i15;
import 'package:http/http.dart' as _i13;
import 'package:mockito/mockito.dart' as _i1; import 'package:mockito/mockito.dart' as _i1;
import 'package:stackwallet/models/exchange/change_now/available_floating_rate_pair.dart'
as _i19;
import 'package:stackwallet/models/exchange/change_now/change_now_response.dart'
as _i2;
import 'package:stackwallet/models/exchange/change_now/currency.dart' as _i14;
import 'package:stackwallet/models/exchange/change_now/estimated_exchange_amount.dart'
as _i16;
import 'package:stackwallet/models/exchange/change_now/exchange_transaction.dart' import 'package:stackwallet/models/exchange/change_now/exchange_transaction.dart'
as _i9; as _i10;
import 'package:stackwallet/models/exchange/change_now/exchange_transaction_status.dart'
as _i18;
import 'package:stackwallet/models/exchange/change_now/fixed_rate_market.dart'
as _i17;
import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart' import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart'
as _i4; as _i5;
import 'package:stackwallet/services/change_now/change_now.dart' as _i11; import 'package:stackwallet/services/change_now/change_now.dart' as _i12;
import 'package:stackwallet/services/trade_notes_service.dart' as _i10; import 'package:stackwallet/services/trade_notes_service.dart' as _i11;
import 'package:stackwallet/services/trade_service.dart' as _i8; import 'package:stackwallet/services/trade_service.dart' as _i9;
import 'package:stackwallet/utilities/enums/backup_frequency_type.dart' as _i5; import 'package:stackwallet/utilities/enums/backup_frequency_type.dart' as _i6;
import 'package:stackwallet/utilities/enums/sync_type_enum.dart' as _i3; import 'package:stackwallet/utilities/enums/sync_type_enum.dart' as _i4;
import 'package:stackwallet/utilities/prefs.dart' as _i2; import 'package:stackwallet/utilities/prefs.dart' as _i3;
// ignore_for_file: type=lint // ignore_for_file: type=lint
// ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_redundant_argument_values
@ -27,10 +40,13 @@ import 'package:stackwallet/utilities/prefs.dart' as _i2;
// ignore_for_file: unnecessary_parenthesis // ignore_for_file: unnecessary_parenthesis
// ignore_for_file: camel_case_types // ignore_for_file: camel_case_types
class _FakeChangeNowResponse_0<T> extends _i1.Fake
implements _i2.ChangeNowResponse<T> {}
/// A class which mocks [Prefs]. /// A class which mocks [Prefs].
/// ///
/// See the documentation for Mockito's code generation for more information. /// See the documentation for Mockito's code generation for more information.
class MockPrefs extends _i1.Mock implements _i2.Prefs { class MockPrefs extends _i1.Mock implements _i3.Prefs {
MockPrefs() { MockPrefs() {
_i1.throwOnMissingStub(this); _i1.throwOnMissingStub(this);
} }
@ -69,11 +85,11 @@ class MockPrefs extends _i1.Mock implements _i2.Prefs {
Invocation.setter(#walletIdsSyncOnStartup, walletIdsSyncOnStartup), Invocation.setter(#walletIdsSyncOnStartup, walletIdsSyncOnStartup),
returnValueForMissingStub: null); returnValueForMissingStub: null);
@override @override
_i3.SyncingType get syncType => _i4.SyncingType get syncType =>
(super.noSuchMethod(Invocation.getter(#syncType), (super.noSuchMethod(Invocation.getter(#syncType),
returnValue: _i3.SyncingType.currentWalletOnly) as _i3.SyncingType); returnValue: _i4.SyncingType.currentWalletOnly) as _i4.SyncingType);
@override @override
set syncType(_i3.SyncingType? syncType) => set syncType(_i4.SyncingType? syncType) =>
super.noSuchMethod(Invocation.setter(#syncType, syncType), super.noSuchMethod(Invocation.setter(#syncType, syncType),
returnValueForMissingStub: null); returnValueForMissingStub: null);
@override @override
@ -109,11 +125,11 @@ class MockPrefs extends _i1.Mock implements _i2.Prefs {
super.noSuchMethod(Invocation.setter(#currency, newCurrency), super.noSuchMethod(Invocation.setter(#currency, newCurrency),
returnValueForMissingStub: null); returnValueForMissingStub: null);
@override @override
_i4.ExchangeRateType get exchangeRateType => _i5.ExchangeRateType get exchangeRateType =>
(super.noSuchMethod(Invocation.getter(#exchangeRateType), (super.noSuchMethod(Invocation.getter(#exchangeRateType),
returnValue: _i4.ExchangeRateType.estimated) as _i4.ExchangeRateType); returnValue: _i5.ExchangeRateType.estimated) as _i5.ExchangeRateType);
@override @override
set exchangeRateType(_i4.ExchangeRateType? exchangeRateType) => set exchangeRateType(_i5.ExchangeRateType? exchangeRateType) =>
super.noSuchMethod(Invocation.setter(#exchangeRateType, exchangeRateType), super.noSuchMethod(Invocation.setter(#exchangeRateType, exchangeRateType),
returnValueForMissingStub: null); returnValueForMissingStub: null);
@override @override
@ -153,12 +169,12 @@ class MockPrefs extends _i1.Mock implements _i2.Prefs {
Invocation.setter(#autoBackupLocation, autoBackupLocation), Invocation.setter(#autoBackupLocation, autoBackupLocation),
returnValueForMissingStub: null); returnValueForMissingStub: null);
@override @override
_i5.BackupFrequencyType get backupFrequencyType => _i6.BackupFrequencyType get backupFrequencyType =>
(super.noSuchMethod(Invocation.getter(#backupFrequencyType), (super.noSuchMethod(Invocation.getter(#backupFrequencyType),
returnValue: _i5.BackupFrequencyType.everyTenMinutes) returnValue: _i6.BackupFrequencyType.everyTenMinutes)
as _i5.BackupFrequencyType); as _i6.BackupFrequencyType);
@override @override
set backupFrequencyType(_i5.BackupFrequencyType? backupFrequencyType) => set backupFrequencyType(_i6.BackupFrequencyType? backupFrequencyType) =>
super.noSuchMethod( super.noSuchMethod(
Invocation.setter(#backupFrequencyType, backupFrequencyType), Invocation.setter(#backupFrequencyType, backupFrequencyType),
returnValueForMissingStub: null); returnValueForMissingStub: null);
@ -171,20 +187,20 @@ class MockPrefs extends _i1.Mock implements _i2.Prefs {
(super.noSuchMethod(Invocation.getter(#hasListeners), returnValue: false) (super.noSuchMethod(Invocation.getter(#hasListeners), returnValue: false)
as bool); as bool);
@override @override
_i6.Future<void> init() => (super.noSuchMethod(Invocation.method(#init, []), _i7.Future<void> init() => (super.noSuchMethod(Invocation.method(#init, []),
returnValue: Future<void>.value(), returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as _i6.Future<void>); returnValueForMissingStub: Future<void>.value()) as _i7.Future<void>);
@override @override
_i6.Future<void> incrementCurrentNotificationIndex() => (super.noSuchMethod( _i7.Future<void> incrementCurrentNotificationIndex() => (super.noSuchMethod(
Invocation.method(#incrementCurrentNotificationIndex, []), Invocation.method(#incrementCurrentNotificationIndex, []),
returnValue: Future<void>.value(), returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as _i6.Future<void>); returnValueForMissingStub: Future<void>.value()) as _i7.Future<void>);
@override @override
void addListener(_i7.VoidCallback? listener) => void addListener(_i8.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#addListener, [listener]), super.noSuchMethod(Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null); returnValueForMissingStub: null);
@override @override
void removeListener(_i7.VoidCallback? listener) => void removeListener(_i8.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#removeListener, [listener]), super.noSuchMethod(Invocation.method(#removeListener, [listener]),
returnValueForMissingStub: null); returnValueForMissingStub: null);
@override @override
@ -199,57 +215,57 @@ class MockPrefs extends _i1.Mock implements _i2.Prefs {
/// A class which mocks [TradesService]. /// A class which mocks [TradesService].
/// ///
/// See the documentation for Mockito's code generation for more information. /// See the documentation for Mockito's code generation for more information.
class MockTradesService extends _i1.Mock implements _i8.TradesService { class MockTradesService extends _i1.Mock implements _i9.TradesService {
MockTradesService() { MockTradesService() {
_i1.throwOnMissingStub(this); _i1.throwOnMissingStub(this);
} }
@override @override
List<_i9.ExchangeTransaction> get trades => List<_i10.ExchangeTransaction> get trades =>
(super.noSuchMethod(Invocation.getter(#trades), (super.noSuchMethod(Invocation.getter(#trades),
returnValue: <_i9.ExchangeTransaction>[]) returnValue: <_i10.ExchangeTransaction>[])
as List<_i9.ExchangeTransaction>); as List<_i10.ExchangeTransaction>);
@override @override
bool get hasListeners => bool get hasListeners =>
(super.noSuchMethod(Invocation.getter(#hasListeners), returnValue: false) (super.noSuchMethod(Invocation.getter(#hasListeners), returnValue: false)
as bool); as bool);
@override @override
_i6.Future<void> add( _i7.Future<void> add(
{_i9.ExchangeTransaction? trade, bool? shouldNotifyListeners}) => {_i10.ExchangeTransaction? trade, bool? shouldNotifyListeners}) =>
(super.noSuchMethod( (super.noSuchMethod(
Invocation.method(#add, [], Invocation.method(#add, [],
{#trade: trade, #shouldNotifyListeners: shouldNotifyListeners}), {#trade: trade, #shouldNotifyListeners: shouldNotifyListeners}),
returnValue: Future<void>.value(), returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as _i6.Future<void>); returnValueForMissingStub: Future<void>.value()) as _i7.Future<void>);
@override @override
_i6.Future<void> edit( _i7.Future<void> edit(
{_i9.ExchangeTransaction? trade, bool? shouldNotifyListeners}) => {_i10.ExchangeTransaction? trade, bool? shouldNotifyListeners}) =>
(super.noSuchMethod( (super.noSuchMethod(
Invocation.method(#edit, [], Invocation.method(#edit, [],
{#trade: trade, #shouldNotifyListeners: shouldNotifyListeners}), {#trade: trade, #shouldNotifyListeners: shouldNotifyListeners}),
returnValue: Future<void>.value(), returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as _i6.Future<void>); returnValueForMissingStub: Future<void>.value()) as _i7.Future<void>);
@override @override
_i6.Future<void> delete( _i7.Future<void> delete(
{_i9.ExchangeTransaction? trade, bool? shouldNotifyListeners}) => {_i10.ExchangeTransaction? trade, bool? shouldNotifyListeners}) =>
(super.noSuchMethod( (super.noSuchMethod(
Invocation.method(#delete, [], Invocation.method(#delete, [],
{#trade: trade, #shouldNotifyListeners: shouldNotifyListeners}), {#trade: trade, #shouldNotifyListeners: shouldNotifyListeners}),
returnValue: Future<void>.value(), returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as _i6.Future<void>); returnValueForMissingStub: Future<void>.value()) as _i7.Future<void>);
@override @override
_i6.Future<void> deleteByUuid({String? uuid, bool? shouldNotifyListeners}) => _i7.Future<void> deleteByUuid({String? uuid, bool? shouldNotifyListeners}) =>
(super.noSuchMethod( (super.noSuchMethod(
Invocation.method(#deleteByUuid, [], Invocation.method(#deleteByUuid, [],
{#uuid: uuid, #shouldNotifyListeners: shouldNotifyListeners}), {#uuid: uuid, #shouldNotifyListeners: shouldNotifyListeners}),
returnValue: Future<void>.value(), returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as _i6.Future<void>); returnValueForMissingStub: Future<void>.value()) as _i7.Future<void>);
@override @override
void addListener(_i7.VoidCallback? listener) => void addListener(_i8.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#addListener, [listener]), super.noSuchMethod(Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null); returnValueForMissingStub: null);
@override @override
void removeListener(_i7.VoidCallback? listener) => void removeListener(_i8.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#removeListener, [listener]), super.noSuchMethod(Invocation.method(#removeListener, [listener]),
returnValueForMissingStub: null); returnValueForMissingStub: null);
@override @override
@ -264,7 +280,7 @@ class MockTradesService extends _i1.Mock implements _i8.TradesService {
/// A class which mocks [TradeNotesService]. /// A class which mocks [TradeNotesService].
/// ///
/// See the documentation for Mockito's code generation for more information. /// See the documentation for Mockito's code generation for more information.
class MockTradeNotesService extends _i1.Mock implements _i10.TradeNotesService { class MockTradeNotesService extends _i1.Mock implements _i11.TradeNotesService {
MockTradeNotesService() { MockTradeNotesService() {
_i1.throwOnMissingStub(this); _i1.throwOnMissingStub(this);
} }
@ -281,21 +297,21 @@ class MockTradeNotesService extends _i1.Mock implements _i10.TradeNotesService {
(super.noSuchMethod(Invocation.method(#getNote, [], {#tradeId: tradeId}), (super.noSuchMethod(Invocation.method(#getNote, [], {#tradeId: tradeId}),
returnValue: '') as String); returnValue: '') as String);
@override @override
_i6.Future<void> set({String? tradeId, String? note}) => (super.noSuchMethod( _i7.Future<void> set({String? tradeId, String? note}) => (super.noSuchMethod(
Invocation.method(#set, [], {#tradeId: tradeId, #note: note}), Invocation.method(#set, [], {#tradeId: tradeId, #note: note}),
returnValue: Future<void>.value(), returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as _i6.Future<void>); returnValueForMissingStub: Future<void>.value()) as _i7.Future<void>);
@override @override
_i6.Future<void> delete({String? tradeId}) => _i7.Future<void> delete({String? tradeId}) =>
(super.noSuchMethod(Invocation.method(#delete, [], {#tradeId: tradeId}), (super.noSuchMethod(Invocation.method(#delete, [], {#tradeId: tradeId}),
returnValue: Future<void>.value(), returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as _i6.Future<void>); returnValueForMissingStub: Future<void>.value()) as _i7.Future<void>);
@override @override
void addListener(_i7.VoidCallback? listener) => void addListener(_i8.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#addListener, [listener]), super.noSuchMethod(Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null); returnValueForMissingStub: null);
@override @override
void removeListener(_i7.VoidCallback? listener) => void removeListener(_i8.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#removeListener, [listener]), super.noSuchMethod(Invocation.method(#removeListener, [listener]),
returnValueForMissingStub: null); returnValueForMissingStub: null);
@override @override
@ -310,8 +326,175 @@ class MockTradeNotesService extends _i1.Mock implements _i10.TradeNotesService {
/// A class which mocks [ChangeNow]. /// A class which mocks [ChangeNow].
/// ///
/// See the documentation for Mockito's code generation for more information. /// See the documentation for Mockito's code generation for more information.
class MockChangeNow extends _i1.Mock implements _i11.ChangeNow { class MockChangeNow extends _i1.Mock implements _i12.ChangeNow {
MockChangeNow() { MockChangeNow() {
_i1.throwOnMissingStub(this); _i1.throwOnMissingStub(this);
} }
@override
set client(_i13.Client? _client) =>
super.noSuchMethod(Invocation.setter(#client, _client),
returnValueForMissingStub: null);
@override
_i7.Future<_i2.ChangeNowResponse<List<_i14.Currency>>> getAvailableCurrencies(
{bool? fixedRate, bool? active}) =>
(super.noSuchMethod(
Invocation.method(#getAvailableCurrencies, [],
{#fixedRate: fixedRate, #active: active}),
returnValue: Future<_i2.ChangeNowResponse<List<_i14.Currency>>>.value(
_FakeChangeNowResponse_0<List<_i14.Currency>>())) as _i7
.Future<_i2.ChangeNowResponse<List<_i14.Currency>>>);
@override
_i7.Future<_i2.ChangeNowResponse<List<_i14.Currency>>> getPairedCurrencies(
{String? ticker, bool? fixedRate}) =>
(super.noSuchMethod(
Invocation.method(#getPairedCurrencies, [],
{#ticker: ticker, #fixedRate: fixedRate}),
returnValue: Future<_i2.ChangeNowResponse<List<_i14.Currency>>>.value(
_FakeChangeNowResponse_0<List<_i14.Currency>>())) as _i7
.Future<_i2.ChangeNowResponse<List<_i14.Currency>>>);
@override
_i7.Future<_i2.ChangeNowResponse<_i15.Decimal>> getMinimalExchangeAmount(
{String? fromTicker, String? toTicker, String? apiKey}) =>
(super.noSuchMethod(
Invocation.method(#getMinimalExchangeAmount, [], {
#fromTicker: fromTicker,
#toTicker: toTicker,
#apiKey: apiKey
}),
returnValue: Future<_i2.ChangeNowResponse<_i15.Decimal>>.value(
_FakeChangeNowResponse_0<_i15.Decimal>()))
as _i7.Future<_i2.ChangeNowResponse<_i15.Decimal>>);
@override
_i7.Future<_i2.ChangeNowResponse<_i16.EstimatedExchangeAmount>>
getEstimatedExchangeAmount(
{String? fromTicker,
String? toTicker,
_i15.Decimal? fromAmount,
String? apiKey}) =>
(super.noSuchMethod(
Invocation.method(#getEstimatedExchangeAmount, [], {
#fromTicker: fromTicker,
#toTicker: toTicker,
#fromAmount: fromAmount,
#apiKey: apiKey
}),
returnValue: Future<
_i2.ChangeNowResponse<
_i16.EstimatedExchangeAmount>>.value(
_FakeChangeNowResponse_0<_i16.EstimatedExchangeAmount>()))
as _i7
.Future<_i2.ChangeNowResponse<_i16.EstimatedExchangeAmount>>);
@override
_i7.Future<_i2.ChangeNowResponse<_i16.EstimatedExchangeAmount>>
getEstimatedFixedRateExchangeAmount(
{String? fromTicker,
String? toTicker,
_i15.Decimal? fromAmount,
bool? useRateId = true,
String? apiKey}) =>
(super.noSuchMethod(
Invocation.method(#getEstimatedFixedRateExchangeAmount, [], {
#fromTicker: fromTicker,
#toTicker: toTicker,
#fromAmount: fromAmount,
#useRateId: useRateId,
#apiKey: apiKey
}),
returnValue: Future<
_i2.ChangeNowResponse<
_i16.EstimatedExchangeAmount>>.value(
_FakeChangeNowResponse_0<_i16.EstimatedExchangeAmount>()))
as _i7
.Future<_i2.ChangeNowResponse<_i16.EstimatedExchangeAmount>>);
@override
_i7.Future<_i2.ChangeNowResponse<List<_i17.FixedRateMarket>>>
getAvailableFixedRateMarkets({String? apiKey}) => (super.noSuchMethod(
Invocation.method(
#getAvailableFixedRateMarkets, [], {#apiKey: apiKey}),
returnValue:
Future<_i2.ChangeNowResponse<List<_i17.FixedRateMarket>>>.value(
_FakeChangeNowResponse_0<List<_i17.FixedRateMarket>>())) as _i7
.Future<_i2.ChangeNowResponse<List<_i17.FixedRateMarket>>>);
@override
_i7.Future<_i2.ChangeNowResponse<_i10.ExchangeTransaction>>
createStandardExchangeTransaction(
{String? fromTicker,
String? toTicker,
String? receivingAddress,
_i15.Decimal? amount,
String? extraId = r'',
String? userId = r'',
String? contactEmail = r'',
String? refundAddress = r'',
String? refundExtraId = r'',
String? apiKey}) =>
(super.noSuchMethod(
Invocation.method(#createStandardExchangeTransaction, [], {
#fromTicker: fromTicker,
#toTicker: toTicker,
#receivingAddress: receivingAddress,
#amount: amount,
#extraId: extraId,
#userId: userId,
#contactEmail: contactEmail,
#refundAddress: refundAddress,
#refundExtraId: refundExtraId,
#apiKey: apiKey
}),
returnValue: Future<
_i2.ChangeNowResponse<_i10.ExchangeTransaction>>.value(
_FakeChangeNowResponse_0<_i10.ExchangeTransaction>())) as _i7
.Future<_i2.ChangeNowResponse<_i10.ExchangeTransaction>>);
@override
_i7.Future<_i2.ChangeNowResponse<_i10.ExchangeTransaction>>
createFixedRateExchangeTransaction(
{String? fromTicker,
String? toTicker,
String? receivingAddress,
_i15.Decimal? amount,
String? rateId,
String? extraId = r'',
String? userId = r'',
String? contactEmail = r'',
String? refundAddress = r'',
String? refundExtraId = r'',
String? apiKey}) =>
(super.noSuchMethod(
Invocation.method(#createFixedRateExchangeTransaction, [], {
#fromTicker: fromTicker,
#toTicker: toTicker,
#receivingAddress: receivingAddress,
#amount: amount,
#rateId: rateId,
#extraId: extraId,
#userId: userId,
#contactEmail: contactEmail,
#refundAddress: refundAddress,
#refundExtraId: refundExtraId,
#apiKey: apiKey
}),
returnValue: Future<
_i2.ChangeNowResponse<_i10.ExchangeTransaction>>.value(
_FakeChangeNowResponse_0<_i10.ExchangeTransaction>())) as _i7
.Future<_i2.ChangeNowResponse<_i10.ExchangeTransaction>>);
@override
_i7.Future<_i2.ChangeNowResponse<_i18.ExchangeTransactionStatus>>
getTransactionStatus({String? id, String? apiKey}) => (super.noSuchMethod(
Invocation.method(
#getTransactionStatus, [], {#id: id, #apiKey: apiKey}),
returnValue:
Future<_i2.ChangeNowResponse<_i18.ExchangeTransactionStatus>>.value(
_FakeChangeNowResponse_0<_i18.ExchangeTransactionStatus>())) as _i7
.Future<_i2.ChangeNowResponse<_i18.ExchangeTransactionStatus>>);
@override
_i7.Future<_i2.ChangeNowResponse<List<_i19.AvailableFloatingRatePair>>>
getAvailableFloatingRatePairs({bool? includePartners = false}) => (super
.noSuchMethod(
Invocation.method(#getAvailableFloatingRatePairs, [],
{#includePartners: includePartners}),
returnValue:
Future<_i2.ChangeNowResponse<List<_i19.AvailableFloatingRatePair>>>.value(
_FakeChangeNowResponse_0<List<_i19.AvailableFloatingRatePair>>())) as _i7
.Future<_i2.ChangeNowResponse<List<_i19.AvailableFloatingRatePair>>>);
} }

View file

@ -176,4 +176,10 @@ class FakeCoinServiceAPI extends CoinServiceAPI {
// TODO: implement testNetworkConnection // TODO: implement testNetworkConnection
throw UnimplementedError(); throw UnimplementedError();
} }
@override
Future<bool> generateNewAddress() {
// TODO: implement generateNewAddress
throw UnimplementedError();
}
} }