mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-01-10 12:44:31 +00:00
commit
bd4ce2c2ac
26 changed files with 32835 additions and 516 deletions
15
.github/workflows/test.yaml
vendored
15
.github/workflows/test.yaml
vendored
|
@ -1,5 +1,6 @@
|
|||
#should deny
|
||||
name: Test
|
||||
on: [push, pull_request]
|
||||
on: [pull_request]
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-20.04
|
||||
|
@ -13,6 +14,10 @@ jobs:
|
|||
uses: subosito/flutter-action@v2
|
||||
- name: Checkout submodules
|
||||
run: git submodule update --init --recursive
|
||||
- name: Build Lelantus
|
||||
run: |
|
||||
cd crypto_plugins/flutter_liblelantus/scripts/linux/
|
||||
./build_all.sh
|
||||
- name: Get dependencies
|
||||
run: flutter pub get
|
||||
- name: Create temp files
|
||||
|
@ -58,7 +63,13 @@ jobs:
|
|||
# - name: Analyze
|
||||
# run: flutter analyze
|
||||
- name: Test
|
||||
run: flutter test
|
||||
run: flutter test --coverage
|
||||
- name: Upload to code coverage
|
||||
uses: codecov/codecov-action@v1.2.2
|
||||
if: success() || failure()
|
||||
with:
|
||||
token: ${{secrets.CODECOV_TOKEN}}
|
||||
file: coverage/lcov.info
|
||||
- name: Delete temp files
|
||||
run: |
|
||||
Remove-Item -Path $env:CHANGE_NOW;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
[![codecov](https://codecov.io/gh/cypherstack/stack_wallet/branch/main/graph/badge.svg?token=PM1N56UTEW)](https://codecov.io/gh/cypherstack/stack_wallet)
|
||||
|
||||
# Stack Wallet
|
||||
put details here
|
||||
|
||||
|
@ -8,6 +10,7 @@ put features here
|
|||
|
||||
## Build and run
|
||||
### Prerequisites
|
||||
- Flutter 3.0.5
|
||||
- Flutter SDK Requirement (>=2.12.0, up until <3.0.0)
|
||||
- Android/iOS dev setup (Android Studio, xCode and subsequent dependencies)
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@ linter:
|
|||
no_leading_underscores_for_local_identifiers: false
|
||||
no_leading_underscores_for_library_prefixes: false
|
||||
avoid_print: true
|
||||
unawaited_futures: false
|
||||
unawaited_futures: true
|
||||
avoid_double_and_int_checks: false
|
||||
constant_identifier_names: false
|
||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||
|
|
|
@ -53,6 +53,15 @@ android {
|
|||
ndk {
|
||||
abiFilters "x86_64","armeabi-v7a", "arm64-v8a"
|
||||
}
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments "-DANDROID_STL=c++_shared", '-DBUILD_TESTING=OFF', "-DANDROID_TOOLCHAIN=clang -v"
|
||||
cppFlags "-frtti -fexceptions -v -DANDROID -std=c++17"
|
||||
// cppFlags "-std=c++11"
|
||||
version "3.10.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 78533fa427ffc582b83cb67a766c8d38fac2abd8
|
||||
Subproject commit 6242046217abf47b61d9397ae447632b06f853fa
|
|
@ -1 +1 @@
|
|||
Subproject commit 771175e87c629ca1d8db7a4417191bd15012d52f
|
||||
Subproject commit cd6f9cf62afcb6c1e55b16a76374a8577d85352f
|
|
@ -449,7 +449,7 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = 37;
|
||||
CURRENT_PROJECT_VERSION = 42;
|
||||
DEVELOPMENT_TEAM = 4DQKUWSG6C;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
|
@ -503,7 +503,7 @@
|
|||
"$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**",
|
||||
"$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs",
|
||||
);
|
||||
MARKETING_VERSION = 1.4.30;
|
||||
MARKETING_VERSION = 1.4.34;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@ -633,7 +633,7 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = 37;
|
||||
CURRENT_PROJECT_VERSION = 42;
|
||||
DEVELOPMENT_TEAM = 4DQKUWSG6C;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
|
@ -687,7 +687,7 @@
|
|||
"$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**",
|
||||
"$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs",
|
||||
);
|
||||
MARKETING_VERSION = 1.4.30;
|
||||
MARKETING_VERSION = 1.4.34;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@ -709,7 +709,7 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = 37;
|
||||
CURRENT_PROJECT_VERSION = 42;
|
||||
DEVELOPMENT_TEAM = 4DQKUWSG6C;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
|
@ -763,7 +763,7 @@
|
|||
"$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**",
|
||||
"$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs",
|
||||
);
|
||||
MARKETING_VERSION = 1.4.30;
|
||||
MARKETING_VERSION = 1.4.34;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
|
|
@ -176,7 +176,14 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
|||
|
||||
late final Completer<void> loadingCompleter;
|
||||
|
||||
bool didLoad = false;
|
||||
|
||||
Future<void> load() async {
|
||||
if (didLoad) {
|
||||
return;
|
||||
}
|
||||
didLoad = true;
|
||||
|
||||
await DB.instance.init();
|
||||
|
||||
_notificationsService = ref.read(notificationsProvider);
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:stackwallet/pages/add_wallet_views/add_wallet_view/sub_widgets/coin_select_item.dart';
|
||||
|
@ -15,15 +13,10 @@ class AddWalletView extends StatelessWidget {
|
|||
|
||||
static const routeName = "/addWallet";
|
||||
|
||||
final _coins = Coin.values;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
List<Coin> coins = _coins;
|
||||
if (Platform.isIOS) {
|
||||
coins = _coins;
|
||||
}
|
||||
debugPrint("BUILD: $runtimeType");
|
||||
List<Coin> coins = [...Coin.values];
|
||||
coins.remove(Coin.firoTestNet);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'dart:math';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:bip39/bip39.dart' as bip39;
|
||||
import 'package:bip39/src/wordlists/english.dart' as bip39wordlist;
|
||||
|
@ -26,6 +26,7 @@ import 'package:stackwallet/utilities/barcode_scanner_interface.dart';
|
|||
import 'package:stackwallet/utilities/cfcolors.dart';
|
||||
import 'package:stackwallet/utilities/clipboard_interface.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/custom_text_selection_controls.dart';
|
||||
import 'package:stackwallet/utilities/default_nodes.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
|
||||
|
@ -70,25 +71,61 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
|
|||
final ScrollController controller = ScrollController();
|
||||
|
||||
final List<TextEditingController> _controllers = [];
|
||||
// late final TextEditingController _heightController;
|
||||
final List<FormInputStatus> _inputStatuses = [];
|
||||
|
||||
// late final FocusNode _heightFocusNode;
|
||||
|
||||
late final BarcodeScannerInterface scanner;
|
||||
|
||||
late final TextSelectionControls textSelectionControls;
|
||||
|
||||
Future<void> onControlsPaste(TextSelectionDelegate delegate) async {
|
||||
final data = await widget.clipboard.getData(Clipboard.kTextPlain);
|
||||
if (data?.text == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final text = data!.text!.trim();
|
||||
if (text.isEmpty || _controllers.isEmpty) {
|
||||
delegate.pasteText(SelectionChangedCause.toolbar);
|
||||
return;
|
||||
}
|
||||
|
||||
final words = text.split(" ");
|
||||
if (words.isEmpty) {
|
||||
delegate.pasteText(SelectionChangedCause.toolbar);
|
||||
return;
|
||||
}
|
||||
|
||||
if (words.length == 1) {
|
||||
_controllers.first.text = words.first;
|
||||
if (_isValidMnemonicWord(words.first.toLowerCase())) {
|
||||
setState(() {
|
||||
_inputStatuses.first = FormInputStatus.valid;
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
_inputStatuses.first = FormInputStatus.invalid;
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
_clearAndPopulateMnemonic(words);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_seedWordCount = widget.seedWordsLength;
|
||||
|
||||
// _heightFocusNode = FocusNode();
|
||||
textSelectionControls = Platform.isIOS
|
||||
? CustomCupertinoTextSelectionControls(onPaste: onControlsPaste)
|
||||
: CustomMaterialTextSelectionControls(onPaste: onControlsPaste);
|
||||
|
||||
scanner = widget.barcodeScanner;
|
||||
for (int i = 0; i < _seedWordCount; i++) {
|
||||
_controllers.add(TextEditingController());
|
||||
_inputStatuses.add(FormInputStatus.empty);
|
||||
}
|
||||
// _heightController = TextEditingController();
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
|
@ -97,8 +134,7 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
|
|||
for (var element in _controllers) {
|
||||
element.dispose();
|
||||
}
|
||||
// _heightController.dispose();
|
||||
// _heightFocusNode.dispose();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
@ -404,6 +440,9 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
|
|||
_inputStatuses[i] = FormInputStatus.empty;
|
||||
});
|
||||
}
|
||||
|
||||
controller.animateTo(controller.position.maxScrollExtent,
|
||||
duration: const Duration(milliseconds: 300), curve: Curves.decelerate);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -442,18 +481,8 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
|
|||
),
|
||||
onPressed: () async {
|
||||
try {
|
||||
// ref
|
||||
// .read(shouldShowLockscreenOnResumeStateProvider.state)
|
||||
// .state = false;
|
||||
final qrResult = await scanner.scan();
|
||||
|
||||
// Future<void>.delayed(
|
||||
// const Duration(seconds: 2),
|
||||
// () => ref
|
||||
// .read(shouldShowLockscreenOnResumeStateProvider.state)
|
||||
// .state = true,
|
||||
// );
|
||||
|
||||
final results =
|
||||
AddressUtils.decodeQRSeedData(qrResult.rawContent);
|
||||
|
||||
|
@ -474,9 +503,6 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
|
|||
}
|
||||
}
|
||||
} on PlatformException catch (e) {
|
||||
// ref
|
||||
// .read(shouldShowLockscreenOnResumeStateProvider.state)
|
||||
// .state = true;
|
||||
// likely failed to get camera permissions
|
||||
Logging.instance.log("Restore wallet qr scan failed: $e",
|
||||
level: LogLevel.Warning);
|
||||
|
@ -512,9 +538,6 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
|
|||
final content = data.text!.trim();
|
||||
final list = content.split(" ");
|
||||
_clearAndPopulateMnemonic(list);
|
||||
controller.animateTo(controller.position.maxScrollExtent,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.decelerate);
|
||||
}
|
||||
},
|
||||
),
|
||||
|
@ -572,6 +595,8 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
|
|||
_inputStatuses[i - 1], "$i"),
|
||||
autovalidateMode:
|
||||
AutovalidateMode.onUserInteraction,
|
||||
selectionControls:
|
||||
i == 1 ? textSelectionControls : null,
|
||||
onChanged: (value) {
|
||||
if (value.isEmpty) {
|
||||
setState(() {
|
||||
|
|
|
@ -48,7 +48,9 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
|
|||
ref.refresh(addressBookFilterProvider);
|
||||
|
||||
if (widget.coin == null) {
|
||||
final coins = Coin.values.where((e) => !(e == Coin.epicCash)).toList();
|
||||
List<Coin> coins =
|
||||
Coin.values.where((e) => !(e == Coin.epicCash)).toList();
|
||||
coins.remove(Coin.firoTestNet);
|
||||
|
||||
bool showTestNet = ref.read(prefsChangeNotifierProvider).showTestNetCoins;
|
||||
|
||||
|
|
|
@ -23,7 +23,8 @@ class _AddressBookFilterViewState extends ConsumerState<AddressBookFilterView> {
|
|||
|
||||
@override
|
||||
void initState() {
|
||||
final coins = Coin.values;
|
||||
List<Coin> coins = [...Coin.values];
|
||||
coins.remove(Coin.firoTestNet);
|
||||
|
||||
bool showTestNet = ref.read(prefsChangeNotifierProvider).showTestNetCoins;
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@ class CoinSelectSheet extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final maxHeight = MediaQuery.of(context).size.height * 0.60;
|
||||
var coins_ = [...Coin.values];
|
||||
coins_.remove(Coin.firoTestNet);
|
||||
return Container(
|
||||
decoration: const BoxDecoration(
|
||||
color: CFColors.white,
|
||||
|
@ -68,10 +70,10 @@ class CoinSelectSheet extends StatelessWidget {
|
|||
return ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: showTestNet
|
||||
? Coin.values.length
|
||||
: Coin.values.length - kTestNetCoinCount,
|
||||
? coins_.length
|
||||
: coins_.length - kTestNetCoinCount,
|
||||
itemBuilder: (builderContext, index) {
|
||||
final coin = Coin.values[index];
|
||||
final coin = coins_[index];
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: RawMaterialButton(
|
||||
|
|
|
@ -23,11 +23,12 @@ class ManageNodesView extends ConsumerStatefulWidget {
|
|||
}
|
||||
|
||||
class _ManageNodesViewState extends ConsumerState<ManageNodesView> {
|
||||
List<Coin> _coins = Coin.values;
|
||||
List<Coin> _coins = [...Coin.values];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_coins = _coins.toList();
|
||||
_coins.remove(Coin.firoTestNet);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
|
|
|
@ -18,12 +18,15 @@ abstract class ChangeNow {
|
|||
static const String authority = "api.changenow.io";
|
||||
static const String apiVersion = "/v1";
|
||||
|
||||
/// set this to override using standard http client. Useful for testing
|
||||
static http.Client? client;
|
||||
|
||||
static Uri _buildUri(String path, Map<String, dynamic>? params) {
|
||||
return Uri.https(authority, apiVersion + path, params);
|
||||
}
|
||||
|
||||
static Future<dynamic> _makeGetRequest(Uri uri) async {
|
||||
final client = http.Client();
|
||||
final client = ChangeNow.client ?? http.Client();
|
||||
try {
|
||||
final response = await client.get(
|
||||
uri,
|
||||
|
@ -44,7 +47,7 @@ abstract class ChangeNow {
|
|||
Uri uri,
|
||||
Map<String, String> body,
|
||||
) async {
|
||||
final client = http.Client();
|
||||
final client = ChangeNow.client ?? http.Client();
|
||||
try {
|
||||
final response = await client.post(
|
||||
uri,
|
||||
|
@ -205,8 +208,9 @@ abstract class ChangeNow {
|
|||
static Future<ChangeNowResponse<Decimal>> getMinimalExchangeAmount({
|
||||
required String fromTicker,
|
||||
required String toTicker,
|
||||
String? apiKey,
|
||||
}) async {
|
||||
Map<String, dynamic>? params = {"api_key": kChangeNowApiKey};
|
||||
Map<String, dynamic>? params = {"api_key": apiKey ?? kChangeNowApiKey};
|
||||
|
||||
final uri = _buildUri("/min-amount/${fromTicker}_$toTicker", params);
|
||||
|
||||
|
@ -244,8 +248,9 @@ abstract class ChangeNow {
|
|||
required String fromTicker,
|
||||
required String toTicker,
|
||||
required Decimal fromAmount,
|
||||
String? apiKey,
|
||||
}) async {
|
||||
Map<String, dynamic> params = {"api_key": kChangeNowApiKey};
|
||||
Map<String, dynamic> params = {"api_key": apiKey ?? kChangeNowApiKey};
|
||||
|
||||
final uri = _buildUri(
|
||||
"/exchange-amount/${fromAmount.toString()}/${fromTicker}_$toTicker",
|
||||
|
@ -292,9 +297,10 @@ abstract class ChangeNow {
|
|||
// to freeze estimated amount that you got in this method. Current estimated
|
||||
// amount would be valid until time in field "validUntil"
|
||||
bool useRateId = true,
|
||||
String? apiKey,
|
||||
}) async {
|
||||
Map<String, dynamic> params = {
|
||||
"api_key": kChangeNowApiKey,
|
||||
"api_key": apiKey ?? kChangeNowApiKey,
|
||||
"useRateId": useRateId.toString(),
|
||||
};
|
||||
|
||||
|
@ -337,8 +343,11 @@ abstract class ChangeNow {
|
|||
/// time and the market info gets updates, so make sure to refresh the list
|
||||
/// occasionally. One time per minute is sufficient.
|
||||
static Future<ChangeNowResponse<List<FixedRateMarket>>>
|
||||
getAvailableFixedRateMarkets() async {
|
||||
final uri = _buildUri("/market-info/fixed-rate/$kChangeNowApiKey", null);
|
||||
getAvailableFixedRateMarkets({
|
||||
String? apiKey,
|
||||
}) async {
|
||||
final uri = _buildUri(
|
||||
"/market-info/fixed-rate/${apiKey ?? kChangeNowApiKey}", null);
|
||||
|
||||
try {
|
||||
// json array is expected here
|
||||
|
@ -406,6 +415,7 @@ abstract class ChangeNow {
|
|||
String contactEmail = "",
|
||||
String refundAddress = "",
|
||||
String refundExtraId = "",
|
||||
String? apiKey,
|
||||
}) async {
|
||||
final Map<String, String> map = {
|
||||
"from": fromTicker,
|
||||
|
@ -420,14 +430,14 @@ abstract class ChangeNow {
|
|||
"refundExtraId": refundExtraId,
|
||||
};
|
||||
|
||||
final uri = _buildUri("/transactions/$kChangeNowApiKey", null);
|
||||
final uri = _buildUri("/transactions/${apiKey ?? kChangeNowApiKey}", null);
|
||||
|
||||
try {
|
||||
// simple json object is expected here
|
||||
final json = await _makePostRequest(uri, map);
|
||||
|
||||
// pass in date to prevent using default 1970 date
|
||||
json["date"] = DateTime.now();
|
||||
json["date"] = DateTime.now().toString();
|
||||
|
||||
try {
|
||||
final value = ExchangeTransaction.fromJson(
|
||||
|
@ -468,6 +478,7 @@ abstract class ChangeNow {
|
|||
String contactEmail = "",
|
||||
String refundAddress = "",
|
||||
String refundExtraId = "",
|
||||
String? apiKey,
|
||||
}) async {
|
||||
final Map<String, String> map = {
|
||||
"from": fromTicker,
|
||||
|
@ -483,14 +494,15 @@ abstract class ChangeNow {
|
|||
"rateId": rateId,
|
||||
};
|
||||
|
||||
final uri = _buildUri("/transactions/fixed-rate/$kChangeNowApiKey", null);
|
||||
final uri = _buildUri(
|
||||
"/transactions/fixed-rate/${apiKey ?? kChangeNowApiKey}", null);
|
||||
|
||||
try {
|
||||
// simple json object is expected here
|
||||
final json = await _makePostRequest(uri, map);
|
||||
|
||||
// pass in date to prevent using default 1970 date
|
||||
json["date"] = DateTime.now();
|
||||
json["date"] = DateTime.now().toString();
|
||||
|
||||
try {
|
||||
final value = ExchangeTransaction.fromJson(
|
||||
|
@ -520,8 +532,10 @@ abstract class ChangeNow {
|
|||
static Future<ChangeNowResponse<ExchangeTransactionStatus>>
|
||||
getTransactionStatus({
|
||||
required String id,
|
||||
String? apiKey,
|
||||
}) async {
|
||||
final uri = _buildUri("/transactions/$id/$kChangeNowApiKey", null);
|
||||
final uri =
|
||||
_buildUri("/transactions/$id/${apiKey ?? kChangeNowApiKey}", null);
|
||||
|
||||
try {
|
||||
// simple json object is expected here
|
||||
|
|
|
@ -726,7 +726,7 @@ Future<String> _getMintScriptWrapper(
|
|||
}
|
||||
|
||||
Future<void> _setTestnetWrapper(bool isTestnet) async {
|
||||
setTestnet(isTestnet);
|
||||
// setTestnet(isTestnet);
|
||||
}
|
||||
|
||||
/// Handles a single instance of a firo wallet
|
||||
|
@ -2893,6 +2893,7 @@ class FiroWallet extends CoinServiceAPI {
|
|||
}
|
||||
if (Platform.environment["FLUTTER_TEST"] == "true" || integrationTestFlag) {
|
||||
perBatch = 10;
|
||||
numberOfThreads = 4;
|
||||
}
|
||||
|
||||
final receiveDerivationsString =
|
||||
|
@ -3818,7 +3819,7 @@ class FiroWallet extends CoinServiceAPI {
|
|||
|
||||
var sendIndex = 1;
|
||||
if (tx["vout"][0]["value"] != null &&
|
||||
tx["vout"][0]["value"] as int > 0) {
|
||||
Decimal.parse(tx["vout"][0]["value"].toString()) > Decimal.zero) {
|
||||
sendIndex = 0;
|
||||
}
|
||||
tx["amount"] = tx["vout"][sendIndex]["value"];
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:cw_core/monero_amount_format.dart';
|
|||
import 'package:cw_core/monero_transaction_priority.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
import 'package:cw_core/sync_status.dart';
|
||||
import 'package:cw_core/transaction_direction.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_credentials.dart';
|
||||
|
@ -24,6 +25,7 @@ import 'package:flutter_libmonero/monero/monero.dart';
|
|||
import 'package:flutter_libmonero/view_model/send/output.dart' as monero_output;
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:mutex/mutex.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:stackwallet/hive/db.dart';
|
||||
|
@ -121,10 +123,10 @@ class MoneroWallet extends CoinServiceAPI {
|
|||
node: Node(uri: "$host:${node.port}", type: WalletType.monero));
|
||||
|
||||
// TODO: is this sync call needed? Do we need to notify ui here?
|
||||
walletBase?.startSync();
|
||||
await walletBase?.startSync();
|
||||
|
||||
if (shouldRefresh) {
|
||||
refresh();
|
||||
await refresh();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,13 +144,37 @@ class MoneroWallet extends CoinServiceAPI {
|
|||
Future<List<String>> get mnemonic => _getMnemonicList();
|
||||
|
||||
Future<int> get currentNodeHeight async {
|
||||
int currentHeight = await getNodeHeight();
|
||||
try {
|
||||
if (walletBase!.syncStatus! is SyncedSyncStatus &&
|
||||
walletBase!.syncStatus!.progress() == 1.0) {
|
||||
return await walletBase!.getNodeHeight();
|
||||
}
|
||||
} catch (e, s) {}
|
||||
int _height = -1;
|
||||
try {
|
||||
_height = (walletBase!.syncStatus as SyncingSyncStatus).height;
|
||||
} catch (e, s) {
|
||||
Logging.instance.log("$e $s", level: LogLevel.Warning);
|
||||
}
|
||||
|
||||
int blocksRemaining = -1;
|
||||
|
||||
try {
|
||||
blocksRemaining =
|
||||
(walletBase!.syncStatus as SyncingSyncStatus).blocksLeft;
|
||||
} catch (e, s) {
|
||||
Logging.instance.log("$e $s", level: LogLevel.Warning);
|
||||
}
|
||||
int currentHeight = _height + blocksRemaining;
|
||||
if (_height == -1 || blocksRemaining == -1) {
|
||||
currentHeight = int64MaxValue;
|
||||
}
|
||||
final cachedHeight = DB.instance
|
||||
.get<dynamic>(boxName: walletId, key: "storedNodeHeight") as int? ??
|
||||
0;
|
||||
|
||||
if (currentHeight > cachedHeight) {
|
||||
DB.instance.put<dynamic>(
|
||||
if (currentHeight > cachedHeight && currentHeight != int64MaxValue) {
|
||||
await DB.instance.put<dynamic>(
|
||||
boxName: walletId, key: "storedNodeHeight", value: currentHeight);
|
||||
return currentHeight;
|
||||
} else {
|
||||
|
@ -156,29 +182,34 @@ class MoneroWallet extends CoinServiceAPI {
|
|||
}
|
||||
}
|
||||
|
||||
int get currentSyncingHeight {
|
||||
Future<int> get currentSyncingHeight async {
|
||||
//TODO return the tip of the monero blockchain
|
||||
final syncingHeight = getSyncingHeight();
|
||||
try {
|
||||
if (walletBase!.syncStatus! is SyncedSyncStatus &&
|
||||
walletBase!.syncStatus!.progress() == 1.0) {
|
||||
Logging.instance
|
||||
.log("currentSyncingHeight lol", level: LogLevel.Warning);
|
||||
return getSyncingHeight();
|
||||
}
|
||||
} catch (e, s) {}
|
||||
int syncingHeight = -1;
|
||||
try {
|
||||
syncingHeight = (walletBase!.syncStatus as SyncingSyncStatus).height;
|
||||
} catch (e, s) {
|
||||
Logging.instance.log("$e $s", level: LogLevel.Warning);
|
||||
}
|
||||
final cachedHeight =
|
||||
DB.instance.get<dynamic>(boxName: walletId, key: "storedSyncingHeight")
|
||||
as int? ??
|
||||
0;
|
||||
|
||||
if (syncingHeight > cachedHeight) {
|
||||
DB.instance.put<dynamic>(
|
||||
await DB.instance.put<dynamic>(
|
||||
boxName: walletId, key: "storedSyncingHeight", value: syncingHeight);
|
||||
return syncingHeight;
|
||||
} else {
|
||||
return cachedHeight;
|
||||
}
|
||||
|
||||
// try {
|
||||
// final result = await _electrumXClient.getBlockHeadTip();
|
||||
// return result["height"];
|
||||
// } catch (e, s) {
|
||||
// Logging.instance.log("Exception caught in chainHeight: $e\n$s");
|
||||
// return -1;
|
||||
// }
|
||||
}
|
||||
|
||||
Future<void> updateStoredChainHeight({required int newHeight}) async {
|
||||
|
@ -270,63 +301,78 @@ class MoneroWallet extends CoinServiceAPI {
|
|||
|
||||
Timer? syncPercentTimer;
|
||||
|
||||
void stopSyncPercentTimer() {
|
||||
Mutex syncHeightMutex = Mutex();
|
||||
Future<void> stopSyncPercentTimer() async {
|
||||
syncPercentTimer?.cancel();
|
||||
syncPercentTimer = null;
|
||||
}
|
||||
|
||||
void startSyncPercentTimer() {
|
||||
Future<void> startSyncPercentTimer() async {
|
||||
if (syncPercentTimer != null) {
|
||||
return;
|
||||
}
|
||||
syncPercentTimer?.cancel();
|
||||
GlobalEventBus.instance
|
||||
.fire(RefreshPercentChangedEvent(highestPercentCached, walletId));
|
||||
syncPercentTimer = Timer.periodic(const Duration(seconds: 3), (_) async {
|
||||
// int restoreheight = walletBase!.walletInfo.restoreHeight ?? 0;
|
||||
int _height = currentSyncingHeight;
|
||||
int _currentHeight = await currentNodeHeight;
|
||||
|
||||
final int blocksRemaining = _currentHeight - _height;
|
||||
|
||||
GlobalEventBus.instance
|
||||
.fire(BlocksRemainingEvent(blocksRemaining, walletId));
|
||||
|
||||
if (blocksRemaining <= 1 && _currentHeight > 0 && _height > 0) {
|
||||
stopSyncPercentTimer();
|
||||
GlobalEventBus.instance.fire(
|
||||
WalletSyncStatusChangedEvent(
|
||||
WalletSyncStatus.synced,
|
||||
walletId,
|
||||
coin,
|
||||
),
|
||||
);
|
||||
syncPercentTimer = Timer.periodic(const Duration(seconds: 30), (_) async {
|
||||
if (syncHeightMutex.isLocked) {
|
||||
return;
|
||||
}
|
||||
await syncHeightMutex.protect(() async {
|
||||
// int restoreheight = walletBase!.walletInfo.restoreHeight ?? 0;
|
||||
int _height = await currentSyncingHeight;
|
||||
int _currentHeight = await currentNodeHeight;
|
||||
double progress = 0;
|
||||
try {
|
||||
progress = walletBase!.syncStatus!.progress();
|
||||
} catch (e, s) {
|
||||
Logging.instance.log("$e $s", level: LogLevel.Warning);
|
||||
}
|
||||
|
||||
// for some reason this can be 0 which screws up the percent calculation
|
||||
// int64MaxValue is NOT the best value to use here
|
||||
if (_currentHeight < 1) {
|
||||
_currentHeight = int64MaxValue;
|
||||
}
|
||||
final int blocksRemaining = _currentHeight - _height;
|
||||
|
||||
if (_height < 1) {
|
||||
_height = 1;
|
||||
}
|
||||
GlobalEventBus.instance
|
||||
.fire(BlocksRemainingEvent(blocksRemaining, walletId));
|
||||
|
||||
double restorePercent = (_height / _currentHeight).clamp(0.0, 1.0);
|
||||
double highestPercent = highestPercentCached;
|
||||
if (progress == 1 && _currentHeight > 0 && _height > 0) {
|
||||
await stopSyncPercentTimer();
|
||||
GlobalEventBus.instance.fire(
|
||||
WalletSyncStatusChangedEvent(
|
||||
WalletSyncStatus.synced,
|
||||
walletId,
|
||||
coin,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.instance.log(
|
||||
"currentSyncingHeight: $_height, nodeHeight: $_currentHeight, restorePercent: $restorePercent, highestPercentCached: $highestPercentCached",
|
||||
level: LogLevel.Info);
|
||||
// for some reason this can be 0 which screws up the percent calculation
|
||||
// int64MaxValue is NOT the best value to use here
|
||||
if (_currentHeight < 1) {
|
||||
_currentHeight = int64MaxValue;
|
||||
}
|
||||
|
||||
if (restorePercent > 0 && restorePercent <= 1) {
|
||||
if (restorePercent > highestPercent) {
|
||||
if (_height < 1) {
|
||||
_height = 1;
|
||||
}
|
||||
|
||||
double restorePercent = progress;
|
||||
double highestPercent = highestPercentCached;
|
||||
|
||||
Logging.instance.log(
|
||||
"currentSyncingHeight: $_height, nodeHeight: $_currentHeight, restorePercent: $restorePercent, highestPercentCached: $highestPercentCached",
|
||||
level: LogLevel.Info);
|
||||
|
||||
if (restorePercent > 0 && restorePercent <= 1) {
|
||||
// if (restorePercent > highestPercent) {
|
||||
highestPercent = restorePercent;
|
||||
highestPercentCached = restorePercent;
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
GlobalEventBus.instance
|
||||
.fire(RefreshPercentChangedEvent(highestPercent, walletId));
|
||||
GlobalEventBus.instance
|
||||
.fire(RefreshPercentChangedEvent(highestPercent, walletId));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -356,7 +402,7 @@ class MoneroWallet extends CoinServiceAPI {
|
|||
}
|
||||
|
||||
try {
|
||||
startSyncPercentTimer();
|
||||
await startSyncPercentTimer();
|
||||
GlobalEventBus.instance.fire(
|
||||
WalletSyncStatusChangedEvent(
|
||||
WalletSyncStatus.syncing,
|
||||
|
@ -365,26 +411,30 @@ class MoneroWallet extends CoinServiceAPI {
|
|||
),
|
||||
);
|
||||
|
||||
final int _currentSyncingHeight = currentSyncingHeight;
|
||||
final int _currentSyncingHeight = await currentSyncingHeight;
|
||||
final int storedHeight = storedChainHeight;
|
||||
int _currentNodeHeight = await walletBase?.getNodeHeight() ?? 0;
|
||||
if (_currentNodeHeight < 1) {
|
||||
_currentNodeHeight = int64MaxValue;
|
||||
}
|
||||
int _currentNodeHeight = await currentNodeHeight;
|
||||
|
||||
_fetchTransactionData();
|
||||
double progress = 0;
|
||||
try {
|
||||
progress = (walletBase!.syncStatus!).progress();
|
||||
} catch (e, s) {
|
||||
Logging.instance.log("$e $s", level: LogLevel.Warning);
|
||||
}
|
||||
await _fetchTransactionData();
|
||||
|
||||
bool stillSyncing = false;
|
||||
Logging.instance.log(
|
||||
"storedHeight: $storedHeight, _currentSyncingHeight: $_currentSyncingHeight, _currentNodeHeight: $_currentNodeHeight",
|
||||
"storedHeight: $storedHeight, _currentSyncingHeight: $_currentSyncingHeight, _currentNodeHeight: $_currentNodeHeight, progress: $progress, issynced: ${await walletBase!.isConnected()}",
|
||||
level: LogLevel.Info);
|
||||
if (storedHeight + 10 < _currentNodeHeight) {
|
||||
|
||||
if (progress < 1.0) {
|
||||
stillSyncing = true;
|
||||
}
|
||||
|
||||
if (_currentSyncingHeight != storedHeight) {
|
||||
if (_currentSyncingHeight > storedHeight) {
|
||||
// 0 is returned from monero as I assume an error?????
|
||||
if (_currentSyncingHeight != 0) {
|
||||
if (_currentSyncingHeight > 0) {
|
||||
// 0 failed to fetch current height???
|
||||
await updateStoredChainHeight(newHeight: _currentSyncingHeight);
|
||||
}
|
||||
|
@ -403,19 +453,11 @@ class MoneroWallet extends CoinServiceAPI {
|
|||
"Failed to call _generateAddressForChain(0, $curIndex): $e\n$s",
|
||||
level: LogLevel.Error);
|
||||
}
|
||||
//
|
||||
final newTxData = _fetchTransactionData();
|
||||
// final feeObj = _getFees();
|
||||
//
|
||||
final newTxData = await _fetchTransactionData();
|
||||
_transactionData = Future(() => newTxData);
|
||||
//
|
||||
// this._feeObject = Future(() => feeObj);
|
||||
// this._utxoData = Future(() => newUtxoData);
|
||||
//
|
||||
// await getAllTxsToWatch(await newTxData);
|
||||
|
||||
if (isActive || shouldAutoSync) {
|
||||
timer ??= Timer.periodic(const Duration(seconds: 30), (timer) async {
|
||||
timer ??= Timer.periodic(const Duration(seconds: 60), (timer) async {
|
||||
debugPrint("run timer");
|
||||
//TODO: check for new data and refresh if needed. if monero even needs this
|
||||
// chain height check currently broken
|
||||
|
@ -450,7 +492,7 @@ class MoneroWallet extends CoinServiceAPI {
|
|||
refreshMutex = false;
|
||||
return;
|
||||
}
|
||||
stopSyncPercentTimer();
|
||||
await stopSyncPercentTimer();
|
||||
GlobalEventBus.instance.fire(
|
||||
WalletSyncStatusChangedEvent(
|
||||
WalletSyncStatus.synced,
|
||||
|
@ -461,7 +503,7 @@ class MoneroWallet extends CoinServiceAPI {
|
|||
refreshMutex = false;
|
||||
} catch (error, strace) {
|
||||
refreshMutex = false;
|
||||
stopSyncPercentTimer();
|
||||
await stopSyncPercentTimer();
|
||||
GlobalEventBus.instance.fire(
|
||||
NodeConnectionStatusChangedEvent(
|
||||
NodeConnectionStatus.disconnected,
|
||||
|
@ -500,7 +542,7 @@ class MoneroWallet extends CoinServiceAPI {
|
|||
|
||||
@override
|
||||
Future<void> exit() async {
|
||||
stopSyncPercentTimer();
|
||||
await stopSyncPercentTimer();
|
||||
_hasCalledExit = true;
|
||||
isActive = false;
|
||||
await walletBase?.save(prioritySave: true);
|
||||
|
@ -552,6 +594,7 @@ class MoneroWallet extends CoinServiceAPI {
|
|||
}
|
||||
|
||||
Future<String> _generateAddressForChain(int chain, int index) async {
|
||||
//
|
||||
String address = walletBase!.getTransactionAddress(chain, index);
|
||||
|
||||
return address;
|
||||
|
@ -688,7 +731,6 @@ class MoneroWallet extends CoinServiceAPI {
|
|||
await walletBase?.connectToNode(
|
||||
node: Node(uri: "$host:${node.port}", type: WalletType.monero));
|
||||
await walletBase?.startSync();
|
||||
walletBase?.getNodeHeight();
|
||||
await DB.instance
|
||||
.put<dynamic>(boxName: walletId, key: "id", value: _walletId);
|
||||
|
||||
|
@ -806,7 +848,6 @@ class MoneroWallet extends CoinServiceAPI {
|
|||
String? password;
|
||||
try {
|
||||
password = await keysStorage?.getWalletPassword(walletName: _walletId);
|
||||
debugPrint("password $password");
|
||||
} catch (e, s) {
|
||||
debugPrint("Exception was thrown $e $s");
|
||||
throw Exception("Password not found $e, $s");
|
||||
|
@ -999,7 +1040,6 @@ class MoneroWallet extends CoinServiceAPI {
|
|||
await walletBase?.connectToNode(
|
||||
node: Node(uri: "$host:${node.port}", type: WalletType.monero));
|
||||
await walletBase?.rescan(height: credentials.height);
|
||||
await walletBase?.getNodeHeight();
|
||||
} catch (e, s) {
|
||||
Logging.instance.log(
|
||||
"Exception rethrown from recoverFromMnemonic(): $e\n$s",
|
||||
|
@ -1113,13 +1153,12 @@ class MoneroWallet extends CoinServiceAPI {
|
|||
moneroAutosaveTimer = null;
|
||||
timer?.cancel();
|
||||
timer = null;
|
||||
stopSyncPercentTimer();
|
||||
await stopSyncPercentTimer();
|
||||
if (isActive) {
|
||||
String? password;
|
||||
try {
|
||||
password =
|
||||
await keysStorage?.getWalletPassword(walletName: _walletId);
|
||||
debugPrint("password $password");
|
||||
} catch (e, s) {
|
||||
debugPrint("Exception was thrown $e $s");
|
||||
throw Exception("Password not found $e, $s");
|
||||
|
@ -1131,9 +1170,9 @@ class MoneroWallet extends CoinServiceAPI {
|
|||
final host = Uri.parse(node.host).host;
|
||||
await walletBase?.connectToNode(
|
||||
node: Node(uri: "$host:${node.port}", type: WalletType.monero));
|
||||
walletBase?.startSync();
|
||||
await walletBase?.startSync();
|
||||
}
|
||||
refresh();
|
||||
await refresh();
|
||||
}
|
||||
this.isActive = isActive;
|
||||
};
|
||||
|
|
|
@ -167,7 +167,7 @@ class NodeService extends ChangeNotifier {
|
|||
final json = jsonDecode(response.body) as Map;
|
||||
final result = jsonDecode(json['result'] as String);
|
||||
final map = jsonDecode(result as String);
|
||||
Logging.instance.log("Rylee: $map", level: LogLevel.Info);
|
||||
Logging.instance.log(map, level: LogLevel.Info);
|
||||
|
||||
for (final coin in Coin.values) {
|
||||
final nodeList = List<Map<String, dynamic>>.from(
|
||||
|
|
22
lib/utilities/custom_text_selection_controls.dart
Normal file
22
lib/utilities/custom_text_selection_controls.dart
Normal file
|
@ -0,0 +1,22 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CustomMaterialTextSelectionControls
|
||||
extends MaterialTextSelectionControls {
|
||||
CustomMaterialTextSelectionControls({required this.onPaste});
|
||||
ValueChanged<TextSelectionDelegate> onPaste;
|
||||
@override
|
||||
Future<void> handlePaste(final TextSelectionDelegate delegate) async {
|
||||
return onPaste(delegate);
|
||||
}
|
||||
}
|
||||
|
||||
class CustomCupertinoTextSelectionControls
|
||||
extends CupertinoTextSelectionControls {
|
||||
CustomCupertinoTextSelectionControls({required this.onPaste});
|
||||
ValueChanged<TextSelectionDelegate> onPaste;
|
||||
@override
|
||||
Future<void> handlePaste(final TextSelectionDelegate delegate) async {
|
||||
return onPaste(delegate);
|
||||
}
|
||||
}
|
|
@ -22,7 +22,8 @@ enum Coin {
|
|||
firoTestNet,
|
||||
}
|
||||
|
||||
const int kTestNetCoinCount = 3;
|
||||
// remove firotestnet for now
|
||||
const int kTestNetCoinCount = 2;
|
||||
|
||||
extension CoinExt on Coin {
|
||||
String get prettyName {
|
||||
|
|
|
@ -11,7 +11,7 @@ description: Stack Wallet
|
|||
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
||||
# Read more about iOS versioning at
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
version: 1.4.31+38
|
||||
version: 1.4.35+43
|
||||
|
||||
environment:
|
||||
sdk: ">=2.17.0 <3.0.0"
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:hive_test/hive_test.dart';
|
||||
// import 'package:flutter_test/flutter_test.dart';
|
||||
// import 'package:hive_test/hive_test.dart';
|
||||
|
||||
void main() {
|
||||
setUp(() async {
|
||||
await setUpTestHive();
|
||||
});
|
||||
|
||||
// no migration to test yet
|
||||
|
||||
tearDown(() async {
|
||||
await tearDownTestHive();
|
||||
});
|
||||
// setUp(() async {
|
||||
// await setUpTestHive();
|
||||
// });
|
||||
//
|
||||
// // no migration to test yet
|
||||
//
|
||||
// tearDown(() async {
|
||||
// await tearDownTestHive();
|
||||
// });
|
||||
}
|
||||
|
|
31326
test/services/change_now/change_now_sample_data.dart
Normal file
31326
test/services/change_now/change_now_sample_data.dart
Normal file
File diff suppressed because it is too large
Load diff
482
test/services/change_now/change_now_test.dart
Normal file
482
test/services/change_now/change_now_test.dart
Normal file
|
@ -0,0 +1,482 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:decimal/decimal.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:http/http.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/exchange_transaction.dart';
|
||||
import 'package:stackwallet/services/change_now/change_now.dart';
|
||||
|
||||
import 'change_now_sample_data.dart';
|
||||
import 'change_now_test.mocks.dart';
|
||||
|
||||
@GenerateMocks([Client])
|
||||
void main() {
|
||||
group("getAvailableCurrencies", () {
|
||||
test("getAvailableCurrencies succeeds without options", () async {
|
||||
final client = MockClient();
|
||||
ChangeNow.client = client;
|
||||
|
||||
when(client.get(
|
||||
Uri.parse("https://api.changenow.io/v1/currencies"),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
)).thenAnswer((realInvocation) async =>
|
||||
Response(jsonEncode(availableCurrenciesJSON), 200));
|
||||
|
||||
final result = await ChangeNow.getAvailableCurrencies();
|
||||
|
||||
expect(result.exception, null);
|
||||
expect(result.value == null, false);
|
||||
expect(result.value!.length, 538);
|
||||
});
|
||||
|
||||
test("getAvailableCurrencies succeeds with active option", () async {
|
||||
final client = MockClient();
|
||||
ChangeNow.client = client;
|
||||
|
||||
when(client.get(
|
||||
Uri.parse("https://api.changenow.io/v1/currencies?active=true"),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
)).thenAnswer((realInvocation) async =>
|
||||
Response(jsonEncode(availableCurrenciesJSONActive), 200));
|
||||
|
||||
final result = await ChangeNow.getAvailableCurrencies(active: true);
|
||||
|
||||
expect(result.exception, null);
|
||||
expect(result.value == null, false);
|
||||
expect(result.value!.length, 531);
|
||||
});
|
||||
|
||||
test("getAvailableCurrencies succeeds with fixedRate option", () async {
|
||||
final client = MockClient();
|
||||
ChangeNow.client = client;
|
||||
|
||||
when(client.get(
|
||||
Uri.parse("https://api.changenow.io/v1/currencies?fixedRate=true"),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
)).thenAnswer((realInvocation) async =>
|
||||
Response(jsonEncode(availableCurrenciesJSONFixedRate), 200));
|
||||
|
||||
final result = await ChangeNow.getAvailableCurrencies(fixedRate: true);
|
||||
|
||||
expect(result.exception, null);
|
||||
expect(result.value == null, false);
|
||||
expect(result.value!.length, 410);
|
||||
});
|
||||
|
||||
test("getAvailableCurrencies succeeds with fixedRate and active options",
|
||||
() async {
|
||||
final client = MockClient();
|
||||
ChangeNow.client = client;
|
||||
|
||||
when(client.get(
|
||||
Uri.parse(
|
||||
"https://api.changenow.io/v1/currencies?fixedRate=true&active=true"),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
)).thenAnswer((realInvocation) async =>
|
||||
Response(jsonEncode(availableCurrenciesJSONActiveFixedRate), 200));
|
||||
|
||||
final result =
|
||||
await ChangeNow.getAvailableCurrencies(active: true, fixedRate: true);
|
||||
|
||||
expect(result.exception, null);
|
||||
expect(result.value == null, false);
|
||||
expect(result.value!.length, 410);
|
||||
});
|
||||
|
||||
test(
|
||||
"getAvailableCurrencies fails with ChangeNowExceptionType.serializeResponseError",
|
||||
() async {
|
||||
final client = MockClient();
|
||||
ChangeNow.client = client;
|
||||
|
||||
when(client.get(
|
||||
Uri.parse("https://api.changenow.io/v1/currencies"),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
)).thenAnswer((realInvocation) async =>
|
||||
Response('{"some unexpected": "but valid json data"}', 200));
|
||||
|
||||
final result = await ChangeNow.getAvailableCurrencies();
|
||||
|
||||
expect(result.exception!.type,
|
||||
ChangeNowExceptionType.serializeResponseError);
|
||||
expect(result.value == null, true);
|
||||
});
|
||||
|
||||
test("getAvailableCurrencies fails for any other reason", () async {
|
||||
final client = MockClient();
|
||||
ChangeNow.client = client;
|
||||
|
||||
when(client.get(
|
||||
Uri.parse("https://api.changenow.io/v1/currencies"),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
)).thenAnswer((realInvocation) async => Response("", 400));
|
||||
|
||||
final result = await ChangeNow.getAvailableCurrencies();
|
||||
|
||||
expect(result.exception!.type, ChangeNowExceptionType.generic);
|
||||
expect(result.value == null, true);
|
||||
});
|
||||
});
|
||||
|
||||
group("getPairedCurrencies", () {
|
||||
test("getPairedCurrencies succeeds without fixedRate option", () async {
|
||||
final client = MockClient();
|
||||
ChangeNow.client = client;
|
||||
|
||||
when(client.get(
|
||||
Uri.parse("https://api.changenow.io/v1/currencies-to/XMR"),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
)).thenAnswer((realInvocation) async =>
|
||||
Response(jsonEncode(getPairedCurrenciesJSON), 200));
|
||||
|
||||
final result = await ChangeNow.getPairedCurrencies(ticker: "XMR");
|
||||
|
||||
expect(result.exception, null);
|
||||
expect(result.value == null, false);
|
||||
expect(result.value!.length, 537);
|
||||
});
|
||||
|
||||
test("getPairedCurrencies succeeds with fixedRate option", () async {
|
||||
final client = MockClient();
|
||||
ChangeNow.client = client;
|
||||
|
||||
when(client.get(
|
||||
Uri.parse(
|
||||
"https://api.changenow.io/v1/currencies-to/XMR?fixedRate=true"),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
)).thenAnswer((realInvocation) async =>
|
||||
Response(jsonEncode(getPairedCurrenciesJSONFixedRate), 200));
|
||||
|
||||
final result =
|
||||
await ChangeNow.getPairedCurrencies(ticker: "XMR", fixedRate: true);
|
||||
|
||||
expect(result.exception, null);
|
||||
expect(result.value == null, false);
|
||||
expect(result.value!.length, 410);
|
||||
});
|
||||
|
||||
test(
|
||||
"getPairedCurrencies fails with ChangeNowExceptionType.serializeResponseError",
|
||||
() async {
|
||||
final client = MockClient();
|
||||
ChangeNow.client = client;
|
||||
|
||||
when(client.get(
|
||||
Uri.parse("https://api.changenow.io/v1/currencies-to/XMR"),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
)).thenAnswer((realInvocation) async =>
|
||||
Response('[{"some unexpected": "but valid json data"}]', 200));
|
||||
|
||||
final result = await ChangeNow.getPairedCurrencies(ticker: "XMR");
|
||||
|
||||
expect(result.exception!.type,
|
||||
ChangeNowExceptionType.serializeResponseError);
|
||||
expect(result.value == null, true);
|
||||
});
|
||||
|
||||
test("getPairedCurrencies fails for any other reason", () async {
|
||||
final client = MockClient();
|
||||
ChangeNow.client = client;
|
||||
|
||||
when(client.get(
|
||||
Uri.parse("https://api.changenow.io/v1/currencies"),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
)).thenAnswer((realInvocation) async => Response("", 400));
|
||||
|
||||
final result =
|
||||
await ChangeNow.getPairedCurrencies(ticker: "XMR", fixedRate: true);
|
||||
|
||||
expect(result.exception!.type, ChangeNowExceptionType.generic);
|
||||
expect(result.value == null, true);
|
||||
});
|
||||
});
|
||||
|
||||
group("getMinimalExchangeAmount", () {
|
||||
test("getMinimalExchangeAmount succeeds", () async {
|
||||
final client = MockClient();
|
||||
ChangeNow.client = client;
|
||||
|
||||
when(client.get(
|
||||
Uri.parse(
|
||||
"https://api.changenow.io/v1/min-amount/xmr_btc?api_key=testAPIKEY"),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
)).thenAnswer(
|
||||
(realInvocation) async => Response('{"minAmount": 42}', 200));
|
||||
|
||||
final result = await ChangeNow.getMinimalExchangeAmount(
|
||||
fromTicker: "xmr",
|
||||
toTicker: "btc",
|
||||
apiKey: "testAPIKEY",
|
||||
);
|
||||
|
||||
expect(result.exception, null);
|
||||
expect(result.value == null, false);
|
||||
expect(result.value, Decimal.fromInt(42));
|
||||
});
|
||||
|
||||
test(
|
||||
"getMinimalExchangeAmount fails with ChangeNowExceptionType.serializeResponseError",
|
||||
() async {
|
||||
final client = MockClient();
|
||||
ChangeNow.client = client;
|
||||
|
||||
when(client.get(
|
||||
Uri.parse(
|
||||
"https://api.changenow.io/v1/min-amount/xmr_btc?api_key=testAPIKEY"),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
)).thenAnswer((realInvocation) async => Response('{"error": 42}', 200));
|
||||
|
||||
final result = await ChangeNow.getMinimalExchangeAmount(
|
||||
fromTicker: "xmr",
|
||||
toTicker: "btc",
|
||||
apiKey: "testAPIKEY",
|
||||
);
|
||||
|
||||
expect(result.exception!.type,
|
||||
ChangeNowExceptionType.serializeResponseError);
|
||||
expect(result.value == null, true);
|
||||
});
|
||||
|
||||
test("getMinimalExchangeAmount fails for any other reason", () async {
|
||||
final client = MockClient();
|
||||
ChangeNow.client = client;
|
||||
|
||||
when(client.get(
|
||||
Uri.parse(
|
||||
"https://api.changenow.io/v1/min-amount/xmr_btc?api_key=testAPIKEY"),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
)).thenAnswer((realInvocation) async => Response('', 400));
|
||||
|
||||
final result = await ChangeNow.getMinimalExchangeAmount(
|
||||
fromTicker: "xmr",
|
||||
toTicker: "btc",
|
||||
apiKey: "testAPIKEY",
|
||||
);
|
||||
|
||||
expect(result.exception!.type, ChangeNowExceptionType.generic);
|
||||
expect(result.value == null, true);
|
||||
});
|
||||
});
|
||||
|
||||
group("getEstimatedFixedRateExchangeAmount", () {
|
||||
test("getEstimatedFixedRateExchangeAmount succeeds", () async {
|
||||
final client = MockClient();
|
||||
ChangeNow.client = client;
|
||||
|
||||
when(client.get(
|
||||
Uri.parse(
|
||||
"https://api.changenow.io/v1/exchange-amount/fixed-rate/10/xmr_btc?api_key=testAPIKEY&useRateId=true"),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
)).thenAnswer((realInvocation) async =>
|
||||
Response(jsonEncode(estFixedRateExchangeAmountJSON), 200));
|
||||
|
||||
final result = await ChangeNow.getEstimatedFixedRateExchangeAmount(
|
||||
fromTicker: "xmr",
|
||||
toTicker: "btc",
|
||||
fromAmount: Decimal.fromInt(10),
|
||||
apiKey: "testAPIKEY",
|
||||
);
|
||||
|
||||
expect(result.exception, null);
|
||||
expect(result.value == null, false);
|
||||
expect(result.value.toString(),
|
||||
'EstimatedExchangeAmount: {estimatedAmount: 0.07271053, transactionSpeedForecast: 10-60, warningMessage: null, rateId: 1t2W5KBPqhycSJVYpaNZzYWLfMr0kSFe, networkFee: 0.00002408}');
|
||||
});
|
||||
|
||||
test(
|
||||
"getEstimatedFixedRateExchangeAmount fails with ChangeNowExceptionType.serializeResponseError",
|
||||
() async {
|
||||
final client = MockClient();
|
||||
ChangeNow.client = client;
|
||||
|
||||
when(client.get(
|
||||
Uri.parse(
|
||||
"https://api.changenow.io/v1/exchange-amount/fixed-rate/10/xmr_btc?api_key=testAPIKEY&useRateId=true"),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
)).thenAnswer((realInvocation) async => Response('{"error": 42}', 200));
|
||||
|
||||
final result = await ChangeNow.getEstimatedFixedRateExchangeAmount(
|
||||
fromTicker: "xmr",
|
||||
toTicker: "btc",
|
||||
fromAmount: Decimal.fromInt(10),
|
||||
apiKey: "testAPIKEY",
|
||||
);
|
||||
|
||||
expect(result.exception!.type,
|
||||
ChangeNowExceptionType.serializeResponseError);
|
||||
expect(result.value == null, true);
|
||||
});
|
||||
|
||||
test("getEstimatedFixedRateExchangeAmount fails for any other reason",
|
||||
() async {
|
||||
final client = MockClient();
|
||||
ChangeNow.client = client;
|
||||
|
||||
when(client.get(
|
||||
Uri.parse(
|
||||
"https://api.changenow.io/v1/exchange-amount/fixed-rate/10/xmr_btc?api_key=testAPIKEY&useRateId=true"),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
)).thenAnswer((realInvocation) async => Response('', 400));
|
||||
|
||||
final result = await ChangeNow.getEstimatedFixedRateExchangeAmount(
|
||||
fromTicker: "xmr",
|
||||
toTicker: "btc",
|
||||
fromAmount: Decimal.fromInt(10),
|
||||
apiKey: "testAPIKEY",
|
||||
);
|
||||
|
||||
expect(result.exception!.type, ChangeNowExceptionType.generic);
|
||||
expect(result.value == null, true);
|
||||
});
|
||||
});
|
||||
|
||||
group("getAvailableFixedRateMarkets", () {
|
||||
test("getAvailableFixedRateMarkets succeeds", () async {
|
||||
final client = MockClient();
|
||||
ChangeNow.client = client;
|
||||
|
||||
when(client.get(
|
||||
Uri.parse(
|
||||
"https://api.changenow.io/v1/market-info/fixed-rate/testAPIKEY"),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
)).thenAnswer((realInvocation) async =>
|
||||
Response(jsonEncode(fixedRateMarketsJSON), 200));
|
||||
|
||||
final result = await ChangeNow.getAvailableFixedRateMarkets(
|
||||
apiKey: "testAPIKEY",
|
||||
);
|
||||
|
||||
expect(result.exception, null);
|
||||
expect(result.value == null, false);
|
||||
expect(result.value!.length, 237);
|
||||
});
|
||||
|
||||
test(
|
||||
"getAvailableFixedRateMarkets fails with ChangeNowExceptionType.serializeResponseError",
|
||||
() async {
|
||||
final client = MockClient();
|
||||
ChangeNow.client = client;
|
||||
|
||||
when(client.get(
|
||||
Uri.parse(
|
||||
"https://api.changenow.io/v1/market-info/fixed-rate/testAPIKEY"),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
)).thenAnswer((realInvocation) async => Response('{"error": 42}', 200));
|
||||
|
||||
final result = await ChangeNow.getAvailableFixedRateMarkets(
|
||||
apiKey: "testAPIKEY",
|
||||
);
|
||||
|
||||
expect(result.exception!.type,
|
||||
ChangeNowExceptionType.serializeResponseError);
|
||||
expect(result.value == null, true);
|
||||
});
|
||||
|
||||
test("getAvailableFixedRateMarkets fails for any other reason", () async {
|
||||
final client = MockClient();
|
||||
ChangeNow.client = client;
|
||||
|
||||
when(client.get(
|
||||
Uri.parse(
|
||||
"https://api.changenow.io/v1/market-info/fixed-rate/testAPIKEY"),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
)).thenAnswer((realInvocation) async => Response('', 400));
|
||||
|
||||
final result = await ChangeNow.getAvailableFixedRateMarkets(
|
||||
apiKey: "testAPIKEY",
|
||||
);
|
||||
|
||||
expect(result.exception!.type, ChangeNowExceptionType.generic);
|
||||
expect(result.value == null, true);
|
||||
});
|
||||
});
|
||||
|
||||
group("createStandardExchangeTransaction", () {
|
||||
test("createStandardExchangeTransaction succeeds", () async {
|
||||
final client = MockClient();
|
||||
ChangeNow.client = client;
|
||||
|
||||
when(client.post(
|
||||
Uri.parse("https://api.changenow.io/v1/transactions/testAPIKEY"),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body:
|
||||
'{"from":"xmr","to":"btc","address":"bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5","amount":"0.3","flow":"standard","extraId":"","userId":"","contactEmail":"","refundAddress":"888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H","refundExtraId":""}',
|
||||
encoding: null,
|
||||
)).thenAnswer((realInvocation) async =>
|
||||
Response(jsonEncode(createStandardTransactionResponse), 200));
|
||||
|
||||
final result = await ChangeNow.createStandardExchangeTransaction(
|
||||
fromTicker: "xmr",
|
||||
toTicker: "btc",
|
||||
receivingAddress: "bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5",
|
||||
amount: Decimal.parse("0.3"),
|
||||
refundAddress:
|
||||
"888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H",
|
||||
apiKey: "testAPIKEY",
|
||||
);
|
||||
|
||||
expect(result.exception, null);
|
||||
expect(result.value == null, false);
|
||||
expect(result.value, isA<ExchangeTransaction>());
|
||||
});
|
||||
|
||||
test(
|
||||
"createStandardExchangeTransaction fails with ChangeNowExceptionType.serializeResponseError",
|
||||
() async {
|
||||
final client = MockClient();
|
||||
ChangeNow.client = client;
|
||||
|
||||
when(client.post(
|
||||
Uri.parse("https://api.changenow.io/v1/transactions/testAPIKEY"),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body:
|
||||
'{"from":"xmr","to":"btc","address":"bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5","amount":"0.3","flow":"standard","extraId":"","userId":"","contactEmail":"","refundAddress":"888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H","refundExtraId":""}',
|
||||
encoding: null,
|
||||
)).thenAnswer((realInvocation) async => Response('{"error": 42}', 200));
|
||||
|
||||
final result = await ChangeNow.createStandardExchangeTransaction(
|
||||
fromTicker: "xmr",
|
||||
toTicker: "btc",
|
||||
receivingAddress: "bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5",
|
||||
amount: Decimal.parse("0.3"),
|
||||
refundAddress:
|
||||
"888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H",
|
||||
apiKey: "testAPIKEY",
|
||||
);
|
||||
|
||||
expect(result.exception!.type,
|
||||
ChangeNowExceptionType.serializeResponseError);
|
||||
expect(result.value == null, true);
|
||||
});
|
||||
|
||||
test("createStandardExchangeTransaction fails for any other reason",
|
||||
() async {
|
||||
final client = MockClient();
|
||||
ChangeNow.client = client;
|
||||
|
||||
when(client.post(
|
||||
Uri.parse("https://api.changenow.io/v1/transactions/testAPIKEY"),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body:
|
||||
'{"from":"xmr","to":"btc","address":"bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5","amount":"0.3","flow":"standard","extraId":"","userId":"","contactEmail":"","refundAddress":"888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H","refundExtraId":""}',
|
||||
encoding: null,
|
||||
)).thenAnswer((realInvocation) async => Response('', 400));
|
||||
|
||||
final result = await ChangeNow.createStandardExchangeTransaction(
|
||||
fromTicker: "xmr",
|
||||
toTicker: "btc",
|
||||
receivingAddress: "bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5",
|
||||
amount: Decimal.parse("0.3"),
|
||||
refundAddress:
|
||||
"888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H",
|
||||
apiKey: "testAPIKEY",
|
||||
);
|
||||
|
||||
expect(result.exception!.type, ChangeNowExceptionType.generic);
|
||||
expect(result.value == null, true);
|
||||
});
|
||||
});
|
||||
}
|
108
test/services/change_now/change_now_test.mocks.dart
Normal file
108
test/services/change_now/change_now_test.mocks.dart
Normal file
|
@ -0,0 +1,108 @@
|
|||
// Mocks generated by Mockito 5.2.0 from annotations
|
||||
// in stackwallet/test/services/change_now_test.dart.
|
||||
// Do not manually edit this file.
|
||||
|
||||
import 'dart:async' as _i5;
|
||||
import 'dart:convert' as _i6;
|
||||
import 'dart:typed_data' as _i7;
|
||||
|
||||
import 'package:http/src/base_request.dart' as _i8;
|
||||
import 'package:http/src/client.dart' as _i4;
|
||||
import 'package:http/src/response.dart' as _i2;
|
||||
import 'package:http/src/streamed_response.dart' as _i3;
|
||||
import 'package:mockito/mockito.dart' as _i1;
|
||||
|
||||
// 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 _FakeResponse_0 extends _i1.Fake implements _i2.Response {}
|
||||
|
||||
class _FakeStreamedResponse_1 extends _i1.Fake implements _i3.StreamedResponse {
|
||||
}
|
||||
|
||||
/// A class which mocks [Client].
|
||||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockClient extends _i1.Mock implements _i4.Client {
|
||||
MockClient() {
|
||||
_i1.throwOnMissingStub(this);
|
||||
}
|
||||
|
||||
@override
|
||||
_i5.Future<_i2.Response> head(Uri? url, {Map<String, String>? headers}) =>
|
||||
(super.noSuchMethod(Invocation.method(#head, [url], {#headers: headers}),
|
||||
returnValue: Future<_i2.Response>.value(_FakeResponse_0()))
|
||||
as _i5.Future<_i2.Response>);
|
||||
@override
|
||||
_i5.Future<_i2.Response> get(Uri? url, {Map<String, String>? headers}) =>
|
||||
(super.noSuchMethod(Invocation.method(#get, [url], {#headers: headers}),
|
||||
returnValue: Future<_i2.Response>.value(_FakeResponse_0()))
|
||||
as _i5.Future<_i2.Response>);
|
||||
@override
|
||||
_i5.Future<_i2.Response> post(Uri? url,
|
||||
{Map<String, String>? headers,
|
||||
Object? body,
|
||||
_i6.Encoding? encoding}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#post, [url],
|
||||
{#headers: headers, #body: body, #encoding: encoding}),
|
||||
returnValue: Future<_i2.Response>.value(_FakeResponse_0()))
|
||||
as _i5.Future<_i2.Response>);
|
||||
@override
|
||||
_i5.Future<_i2.Response> put(Uri? url,
|
||||
{Map<String, String>? headers,
|
||||
Object? body,
|
||||
_i6.Encoding? encoding}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#put, [url],
|
||||
{#headers: headers, #body: body, #encoding: encoding}),
|
||||
returnValue: Future<_i2.Response>.value(_FakeResponse_0()))
|
||||
as _i5.Future<_i2.Response>);
|
||||
@override
|
||||
_i5.Future<_i2.Response> patch(Uri? url,
|
||||
{Map<String, String>? headers,
|
||||
Object? body,
|
||||
_i6.Encoding? encoding}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#patch, [url],
|
||||
{#headers: headers, #body: body, #encoding: encoding}),
|
||||
returnValue: Future<_i2.Response>.value(_FakeResponse_0()))
|
||||
as _i5.Future<_i2.Response>);
|
||||
@override
|
||||
_i5.Future<_i2.Response> delete(Uri? url,
|
||||
{Map<String, String>? headers,
|
||||
Object? body,
|
||||
_i6.Encoding? encoding}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#delete, [url],
|
||||
{#headers: headers, #body: body, #encoding: encoding}),
|
||||
returnValue: Future<_i2.Response>.value(_FakeResponse_0()))
|
||||
as _i5.Future<_i2.Response>);
|
||||
@override
|
||||
_i5.Future<String> read(Uri? url, {Map<String, String>? headers}) =>
|
||||
(super.noSuchMethod(Invocation.method(#read, [url], {#headers: headers}),
|
||||
returnValue: Future<String>.value('')) as _i5.Future<String>);
|
||||
@override
|
||||
_i5.Future<_i7.Uint8List> readBytes(Uri? url,
|
||||
{Map<String, String>? headers}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#readBytes, [url], {#headers: headers}),
|
||||
returnValue: Future<_i7.Uint8List>.value(_i7.Uint8List(0)))
|
||||
as _i5.Future<_i7.Uint8List>);
|
||||
@override
|
||||
_i5.Future<_i3.StreamedResponse> send(_i8.BaseRequest? request) =>
|
||||
(super.noSuchMethod(Invocation.method(#send, [request]),
|
||||
returnValue:
|
||||
Future<_i3.StreamedResponse>.value(_FakeStreamedResponse_1()))
|
||||
as _i5.Future<_i3.StreamedResponse>);
|
||||
@override
|
||||
void close() => super.noSuchMethod(Invocation.method(#close, []),
|
||||
returnValueForMissingStub: null);
|
||||
}
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue